t_algebra 0.1.5 → 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 617977f1693f665231e2b42e517fc64a92de79bbe8824408501586b992308988
4
- data.tar.gz: d953427b001daff3bbfab23e94ce46fcd61c8a386f891d14019312d0ae37ddca
3
+ metadata.gz: 2c3ba123bba6480b11a43629310dcf4caa7f3c1995a8ca54a3694cec83aeb868
4
+ data.tar.gz: 3c47a5a18b15088b2d82d05c0662bda9c68fc72ca60085d7a3d3557a22c760eb
5
5
  SHA512:
6
- metadata.gz: 98fb648dfe43e767057bf50f1479ace8a777b01bf0803b46b9e29c8668aaea8f8464014a7282c74796d1d635b2c4afddec828fb5e85d07c0be44ff6fc85595f0
7
- data.tar.gz: e9ac940c79d2906bf4f27c902474966e8175e1497b98579a0793400f374568c2022f7f804014ba3fd8bbc9f7ddfa84de46ff75dca0b42da9943a0a482d554f15
6
+ metadata.gz: 6850ff73905c72cd22587c2c19d93cf97bc7eebd6543c33e5094e996e2e6e416499a85db1f482ccc37363a4aaa100a2d8f7409480d1b2b0376451a8945d2877d
7
+ data.tar.gz: 863dc96cd733cec7966a58d21ce8d9c1c8123850743975983fb630d4b71d94ef1a9267c3d436e0800eabbe751c10532a95413e3b2a10184c213b3ec763b084bb
data/Gemfile.lock CHANGED
@@ -1,12 +1,19 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- t_algebra (0.1.5)
4
+ t_algebra (1.1.1)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
+ benchmark-ips (2.9.2)
10
+ concurrent-ruby (1.1.9)
9
11
  diff-lcs (1.4.4)
12
+ dry-core (0.7.1)
13
+ concurrent-ruby (~> 1.0)
14
+ dry-monads (1.4.0)
15
+ concurrent-ruby (~> 1.0)
16
+ dry-core (~> 0.7)
10
17
  rake (12.3.3)
11
18
  rspec (3.10.0)
12
19
  rspec-core (~> 3.10.0)
@@ -26,6 +33,8 @@ PLATFORMS
26
33
  ruby
27
34
 
28
35
  DEPENDENCIES
36
+ benchmark-ips
37
+ dry-monads
29
38
  rake (~> 12.0)
30
39
  rspec (~> 3.0)
31
40
  t_algebra!
data/README.md CHANGED
@@ -43,17 +43,18 @@ end
43
43
  ```
44
44
 
45
45
  The class `TAlgebra::Monad::Maybe` wraps a result which may or may not be nil, and uses the Monad tech of `bind` + `fmap` to
46
- sensibly manipulate that result. The below example uses `run/yield` notation (analagous to Haskell `do` notation or js `async/await`)
46
+ sensibly manipulate that result. The below example uses `chain/bound` notation (analagous to Haskell `do/<-` notation or js `async/await`)
47
47
  to reproduce the same functionality:
48
48
 
49
49
  ```
50
50
  # @return [TAlgebra::Monad::Maybe]
51
51
  def get_address(user)
52
- TAlgebra::Monad::Maybe.run do |y|
52
+ TAlgebra::Monad::Maybe.chain do
53
53
  m_user = just(user)
54
- street = y.yield { m_user.fetch(:street) }
55
- city = y.yield { m_user.fetch(:city) }
56
- state = y.yield { m_user.fetch(:state) }
54
+
55
+ street = bound { m_user.fetch(:street) }
56
+ city = bound { m_user.fetch(:city) }
57
+ state = bound { m_user.fetch(:state) }
57
58
  "#{street}, #{city} #{state.upcase}"
58
59
  end
59
60
  end
@@ -65,16 +66,16 @@ subsequently `Parser` which can validate and map over complex data structure.
65
66
  ```
66
67
  # @return [TAlgebra::Monad::Parser]
67
68
  def get_address(user)
68
- TAlgebra::Monad::Parser.run do |y|
69
+ TAlgebra::Monad::Parser.chain do
69
70
  # validate street is present with `#fetch!`
70
- street = y.yield { fetch!(user, :street) }
71
+ street = bound { fetch!(user, :street) }
71
72
 
72
73
  # validate that the city is a string with is `#is_a?`
73
- city = y.yield { fetch!(user, :city).is_a?(String) }
74
+ city = bound { fetch!(user, :city).is_a?(String) }
74
75
 
75
76
  # validate that the state is a 2 letter long string with `#validate`
76
77
  # and transform to all caps with `#fmap`
77
- state = y.yield do
78
+ state = bound do
78
79
  fetch!(user, :state)
79
80
  .is_a?(String)
80
81
  .validate("Is 2 letter code"){ |state| state.length == 2 }
@@ -97,7 +98,8 @@ class ExtAPI
97
98
 
98
99
  class << self
99
100
  def call(verb, path)
100
- ... make api cal
101
+ result = ... make api cal
102
+ new(success: result)
101
103
  rescue => e
102
104
  new(http_error: {e.status, e.message})
103
105
  end
@@ -122,16 +124,16 @@ And it could be used like:
122
124
 
123
125
  ```
124
126
  def user_cities
125
- ExtAPI.run do |y|
126
- users = y.yield { call('GET', '/users') }
127
- profiles = y.yield { call('GET', "/users/profile?id=#{users.map(&:id).to_json}" }
127
+ ExtAPI.chain do
128
+ users = bound { call('GET', '/users') }
129
+ profiles = bound { call('GET', "/users/profile?id=#{users.map(&:id).to_json}" }
128
130
  profiles.map(&:city).uniq
129
131
  end
130
132
  end
131
133
  ```
132
134
 
133
- (Note that while the concept of Promise is monadic, the above ExtAPI implementation is entirely synchronous.) Should
134
- either of the two calls fail, this method will return that error status and message to be handled by the client code.
135
+ Should either of the two calls fail, this method will return that error status and message to be handled by the client code.
136
+ (Note that while the concept of Promise is monadic, the above ExtAPI implementation is entirely synchronous.)
135
137
 
136
138
  ## Development
137
139
 
@@ -1,7 +1,7 @@
1
1
  module TAlgebra
2
2
  module Monad
3
3
  class Either
4
- include TAlgebra::Monad
4
+ include TAlgebra::Monad::SingleValued
5
5
 
6
6
  LEFT = :left
7
7
  RIGHT = :right
@@ -1,7 +1,7 @@
1
1
  module TAlgebra
2
2
  module Monad
3
3
  class Maybe
4
- include TAlgebra::Monad
4
+ include TAlgebra::Monad::SingleValued
5
5
 
6
6
  NOTHING = :nothing
7
7
  JUST = :just
@@ -19,12 +19,6 @@ module TAlgebra
19
19
  def to_maybe(value_or_nil)
20
20
  value_or_nil.nil? ? nothing : pure(value_or_nil)
21
21
  end
22
-
23
- # def run_bind(ma, &block)
24
- # # raise "Yield blocks must return instances of #{self}. Got #{ma.class}" unless [Parser, Parser::Optional].include?(ma.class)
25
- #
26
- # ma.bind(&block)
27
- # end
28
22
  end
29
23
 
30
24
  def fmap(&block)
@@ -1,7 +1,7 @@
1
1
  module TAlgebra
2
2
  module Monad
3
3
  class Parser < Either
4
- include TAlgebra::Monad
4
+ include TAlgebra::Monad::SingleValued
5
5
 
6
6
  def initialize(is:, value:, name: nil)
7
7
  super(is: is, value: value)
@@ -25,7 +25,7 @@ module TAlgebra
25
25
  parse(o).fetch!(key)
26
26
  end
27
27
 
28
- def run_bind(ma, &block)
28
+ def chain_bind(ma, &block)
29
29
  raise "Yield blocks must return instances of #{self}. Got #{ma.class}" unless [Parser, Parser::Optional].include?(ma.class)
30
30
 
31
31
  ma.as_parser.bind(&block)
@@ -0,0 +1,36 @@
1
+ require "fiber"
2
+ # @abstract
3
+ module TAlgebra
4
+ module Monad
5
+ module SingleValued
6
+ module Static
7
+ def chain(&block)
8
+ receiver = augmented_receiver(block)
9
+ fiber = Fiber.new { receiver.instance_exec(&block) }
10
+ chain_recursive(fiber, [])
11
+ end
12
+
13
+ private
14
+
15
+ def chain_recursive(fiber, current)
16
+ val = fiber.resume current
17
+
18
+ if fiber.alive?
19
+ chain_bind(val.call) { |subsequent| chain_recursive(fiber, subsequent) }
20
+ else
21
+ val.is_a?(self.class) ? val : pure(val)
22
+ end
23
+ end
24
+ end
25
+
26
+ class << self
27
+ def included(base)
28
+ base.class_eval do
29
+ include TAlgebra::Monad
30
+ end
31
+ base.extend(Static)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -1,3 +1,4 @@
1
+ require "fiber"
1
2
  # @abstract
2
3
  module TAlgebra
3
4
  module Monad
@@ -6,17 +7,19 @@ module TAlgebra
6
7
  end
7
8
 
8
9
  module Static
9
- class LazyYielder
10
- def initialize(yielder)
11
- @yielder = yielder
12
- end
10
+ def chain(&block)
11
+ receiver = augmented_receiver(block)
12
+ fiber_initializer = -> { Fiber.new { receiver.instance_exec(&block) } }
13
+ chain_recursive(fiber_initializer, [])
14
+ end
15
+ alias_method :run, :chain
13
16
 
14
- def yield(&block)
15
- @yielder.yield(block)
16
- end
17
+ def bound(&block)
18
+ Fiber.yield(block)
17
19
  end
20
+ alias_method :_pick, :bound
18
21
 
19
- def run(&block)
22
+ def augmented_receiver(block)
20
23
  block_self = block.binding.receiver
21
24
 
22
25
  self_class = self
@@ -24,56 +27,38 @@ module TAlgebra
24
27
  self_class.send(m, *args, &block)
25
28
  end
26
29
 
27
- e = Enumerator.new { |y| block_self.instance_exec(LazyYielder.new(y), &block) }
28
-
29
- run_recursive(e, [])
30
+ block_self
30
31
  end
31
32
 
32
- def run_bind(ma, &block)
33
+ def chain_bind(ma, &block)
33
34
  raise "Yield blocks must return instances of #{self}" unless ma.instance_of?(self)
34
35
  ma.bind(&block)
35
36
  end
36
37
 
37
- def lift_a2(ma, mb, &block)
38
+ def lift_a2(ma, mb)
38
39
  ma.bind do |a|
39
40
  mb.bind do |b|
40
- pure(block.call(a, b))
41
+ pure(yield(a, b))
41
42
  end
42
43
  end
43
44
  end
44
45
 
45
46
  private
46
47
 
47
- def run_recursive(enum, historical_values)
48
- enum.rewind
48
+ def chain_recursive(fiber_initializer, historical_values)
49
+ fiber = fiber_initializer.call
49
50
 
51
+ val = fiber.resume
50
52
  historical_values.each do |h|
51
- enum.next
52
- enum.feed(h)
53
+ val = fiber.resume h
53
54
  end
54
55
 
55
- if is_complete(enum)
56
- val = value(enum)
57
- val.is_a?(self.class) ? val : pure(val)
56
+ if fiber.alive?
57
+ chain_bind(val.call) { |a| chain_recursive(fiber_initializer, historical_values + [a]) }
58
58
  else
59
- run_bind(enum.next.call) do |a|
60
- run_recursive(enum, historical_values + [a])
61
- end
59
+ val.is_a?(self.class) ? val : pure(val)
62
60
  end
63
61
  end
64
-
65
- def is_complete(enumerator)
66
- enumerator.peek
67
- false
68
- rescue StopIteration
69
- true
70
- end
71
-
72
- def value(enumerator)
73
- enumerator.peek
74
- rescue StopIteration
75
- $!.result
76
- end
77
62
  end
78
63
 
79
64
  class << self
@@ -1,3 +1,3 @@
1
1
  module TAlgebra
2
- VERSION = "0.1.5"
2
+ VERSION = "0.1.6"
3
3
  end
data/lib/t_algebra.rb CHANGED
@@ -2,7 +2,7 @@ require_relative "t_algebra/version"
2
2
  require_relative "t_algebra/functor"
3
3
  require_relative "t_algebra/applicative"
4
4
  require_relative "t_algebra/monad"
5
- require_relative "t_algebra/monad/errors"
5
+ require_relative "t_algebra/monad/single_valued"
6
6
  require_relative "t_algebra/monad/maybe"
7
7
  require_relative "t_algebra/monad/either"
8
8
  require_relative "t_algebra/monad/list"
@@ -10,5 +10,9 @@ require_relative "t_algebra/monad/reader"
10
10
  require_relative "t_algebra/monad/parser"
11
11
 
12
12
  module TAlgebra
13
- class Error < StandardError; end
13
+ class UseError < StandardError
14
+ end
15
+
16
+ class UnsafeError < StandardError
17
+ end
14
18
  end
data/t_algebra.gemspec CHANGED
@@ -17,6 +17,9 @@ Gem::Specification.new do |spec|
17
17
  spec.metadata["source_code_uri"] = "https://github.com/afg419/t_algebra"
18
18
  spec.metadata["changelog_uri"] = "https://example.com"
19
19
 
20
+ spec.add_development_dependency("dry-monads")
21
+ spec.add_development_dependency("benchmark-ips")
22
+
20
23
  # Specify which files should be added to the gem when it is released.
21
24
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
22
25
  spec.files = Dir.chdir(File.expand_path("..", __FILE__)) do
metadata CHANGED
@@ -1,15 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: t_algebra
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - aaron
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-12-08 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2021-12-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: dry-monads
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: benchmark-ips
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
13
41
  description:
14
42
  email:
15
43
  - afg419@gmail.com
@@ -33,11 +61,11 @@ files:
33
61
  - lib/t_algebra/functor.rb
34
62
  - lib/t_algebra/monad.rb
35
63
  - lib/t_algebra/monad/either.rb
36
- - lib/t_algebra/monad/errors.rb
37
64
  - lib/t_algebra/monad/list.rb
38
65
  - lib/t_algebra/monad/maybe.rb
39
66
  - lib/t_algebra/monad/parser.rb
40
67
  - lib/t_algebra/monad/reader.rb
68
+ - lib/t_algebra/monad/single_valued.rb
41
69
  - lib/t_algebra/version.rb
42
70
  - t_algebra.gemspec
43
71
  homepage: https://github.com/afg419/t_algebra
@@ -1,9 +0,0 @@
1
- module TAlgebra
2
- module Monad
3
- class UseError < StandardError
4
- end
5
-
6
- class UnsafeError < StandardError
7
- end
8
- end
9
- end