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 +4 -4
- data/Gemfile.lock +10 -1
- data/README.md +17 -15
- data/lib/t_algebra/monad/either.rb +1 -1
- data/lib/t_algebra/monad/maybe.rb +1 -7
- data/lib/t_algebra/monad/parser.rb +2 -2
- data/lib/t_algebra/monad/single_valued.rb +36 -0
- data/lib/t_algebra/monad.rb +22 -37
- data/lib/t_algebra/version.rb +1 -1
- data/lib/t_algebra.rb +6 -2
- data/t_algebra.gemspec +3 -0
- metadata +32 -4
- data/lib/t_algebra/monad/errors.rb +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2c3ba123bba6480b11a43629310dcf4caa7f3c1995a8ca54a3694cec83aeb868
|
4
|
+
data.tar.gz: 3c47a5a18b15088b2d82d05c0662bda9c68fc72ca60085d7a3d3557a22c760eb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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 (
|
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 `
|
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.
|
52
|
+
TAlgebra::Monad::Maybe.chain do
|
53
53
|
m_user = just(user)
|
54
|
-
|
55
|
-
|
56
|
-
|
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.
|
69
|
+
TAlgebra::Monad::Parser.chain do
|
69
70
|
# validate street is present with `#fetch!`
|
70
|
-
street =
|
71
|
+
street = bound { fetch!(user, :street) }
|
71
72
|
|
72
73
|
# validate that the city is a string with is `#is_a?`
|
73
|
-
city =
|
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 =
|
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.
|
126
|
-
users =
|
127
|
-
profiles =
|
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
|
-
|
134
|
-
|
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 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
|
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
|
data/lib/t_algebra/monad.rb
CHANGED
@@ -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
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
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
|
-
|
15
|
-
|
16
|
-
end
|
17
|
+
def bound(&block)
|
18
|
+
Fiber.yield(block)
|
17
19
|
end
|
20
|
+
alias_method :_pick, :bound
|
18
21
|
|
19
|
-
def
|
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
|
-
|
28
|
-
|
29
|
-
run_recursive(e, [])
|
30
|
+
block_self
|
30
31
|
end
|
31
32
|
|
32
|
-
def
|
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
|
38
|
+
def lift_a2(ma, mb)
|
38
39
|
ma.bind do |a|
|
39
40
|
mb.bind do |b|
|
40
|
-
pure(
|
41
|
+
pure(yield(a, b))
|
41
42
|
end
|
42
43
|
end
|
43
44
|
end
|
44
45
|
|
45
46
|
private
|
46
47
|
|
47
|
-
def
|
48
|
-
|
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
|
-
|
52
|
-
enum.feed(h)
|
53
|
+
val = fiber.resume h
|
53
54
|
end
|
54
55
|
|
55
|
-
if
|
56
|
-
val
|
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
|
-
|
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
|
data/lib/t_algebra/version.rb
CHANGED
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/
|
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
|
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.
|
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-
|
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
|