t_algebra 0.1.5 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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