deterministic 0.14.1 → 0.15.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,15 +3,15 @@ module Deterministic
3
3
  class NotMonadError < StandardError; end
4
4
 
5
5
  # Basicly the `pure` function
6
- def initialize(value)
7
- @value = join(value)
6
+ def initialize(init)
7
+ @value = join(init)
8
8
  end
9
9
 
10
10
  # If the passed value is monad already, get the value to avoid nesting
11
11
  # M[M[A]] is equivalent to M[A]
12
- def join(value)
13
- if value.is_a? self.class then value.value
14
- else value end
12
+ def join(other)
13
+ if other.is_a? self.class then other.value
14
+ else other end
15
15
  end
16
16
 
17
17
  # The functor: takes a function (a -> b) and applies it to the inner value of the monad (Ma),
@@ -27,6 +27,20 @@ module Deterministic
27
27
  # the self.class, i.e. the containing monad is passed as a second (optional) arg to the function
28
28
  def bind(proc=nil, &block)
29
29
  (proc || block).call(value).tap do |result|
30
+
31
+ # def parent_name(obj)
32
+ # parts = obj.class.name.split('::')
33
+ # if parts.count > 1
34
+ # parts[0..-2].join('::')
35
+ # else
36
+ # parts[0]
37
+ # end
38
+ # end
39
+
40
+ # self_parent = parent_name(self)
41
+ # other_parent = parent_name(result)
42
+ # raise NotMonadError, "Expected #{result.inspect} to be an #{other_parent}" unless self_parent == other_parent
43
+
30
44
  parent = self.class.superclass === Object ? self.class : self.class.superclass
31
45
  raise NotMonadError, "Expected #{result.inspect} to be an #{parent}" unless result.is_a? parent
32
46
  end
@@ -1,28 +1,11 @@
1
1
  module Deterministic
2
- # Abstract parent of Some and None
3
- class Option
4
- include Monad
5
-
6
- module PatternMatching
7
- include Deterministic::PatternMatching
8
- class Match
9
- include Deterministic::PatternMatching::Match
10
-
11
- %w[Some None Option].each do |s|
12
- define_method s.downcase.to_sym do |value=nil, &block|
13
- klas = self.class.module_eval(s)
14
- push(klas, value, block)
15
- end
16
- end
17
- end
18
- end
19
-
20
- include PatternMatching
2
+ Option = Deterministic::enum {
3
+ Some(:s)
4
+ None()
5
+ }
21
6
 
22
- # This is an abstract class, can't ever instantiate it directly
7
+ class Option
23
8
  class << self
24
- protected :new
25
-
26
9
  def some?(expr)
27
10
  to_option(expr) { expr.nil? }
28
11
  end
@@ -39,82 +22,59 @@ module Deterministic
39
22
  yield rescue None.new
40
23
  end
41
24
  end
25
+ end
42
26
 
43
- def map(proc=nil, &block)
44
- return self if none?
45
- bind(proc || block)
46
- end
27
+ impl(Option) {
28
+ class NoneValueError < StandardError; end
47
29
 
48
- ## Convert the inner value to an Array
49
- def value_to_a
50
- map { self.class.new(Array(value)) }
30
+ def fmap(&fn)
31
+ match {
32
+ Some(s) { |m| m.class.new(fn.(s)) }
33
+ None() { |n| n }
34
+ }
51
35
  end
52
36
 
53
- ## Add the inner values of two Some
54
- def +(other)
55
- return other if none?
56
- fmap { |v|
57
- other.match {
58
- some { v + other.value }
59
- none { self }
60
- }
37
+ def map(&fn)
38
+ match {
39
+ Some(s) { |m| m.bind(&fn) }
40
+ None() { |n| n }
61
41
  }
62
42
  end
63
43
 
64
44
  def some?
65
- is_a? Some
45
+ is_a? Option::Some
66
46
  end
67
47
 
68
48
  def none?
69
- is_a? None
49
+ is_a? Option::None
70
50
  end
71
51
 
72
- def value_or(default)
73
- return default if none?
74
- return value
52
+ def value_or(n)
53
+ match {
54
+ Some(s) { s }
55
+ None() { n }
56
+ }
75
57
  end
76
58
 
77
- class Some < Option
78
- class << self; public :new; end
59
+ def value_to_a
60
+ @value
79
61
  end
80
62
 
81
- class None < Option
82
- class << self
83
- public :new
84
-
85
- def instance
86
- @instance ||= new
87
- end
88
- end
89
-
90
- def initialize(*args)
91
- @value = self
92
- end
93
-
94
- def inspect
95
- "None"
96
- end
97
-
98
- private :value
99
-
100
- def fmap(*args)
101
- self
102
- end
103
-
104
- alias :map :fmap
105
-
106
- def ==(other)
107
- other.class == self.class
108
- end
63
+ def +(other)
64
+ match {
65
+ None() { other }
66
+ Some(_, where { !other.is_a?(Option)}) { raise TypeError, "Other must be an #{Option}"}
67
+ Some(s, where { other.some? }) { Option::Some.new(s + other.value) }
68
+ Some(_) { |s| s }
69
+ }
109
70
  end
110
- end
71
+ }
111
72
 
112
- module_function
113
- def Some(value)
114
- Option::Some.new(value)
73
+ module Prelude
74
+ module Option
75
+ None = Deterministic::Option::None.new
76
+ def Some(s); Deterministic::Option::Some.new(s); end
77
+ def None(); Deterministic::Prelude::Option::None; end
78
+ end
115
79
  end
116
-
117
- Some = Deterministic::Option::Some
118
- None = Deterministic::Option::None.instance
119
80
  end
120
- # p Deterministic::Option::Some::Match.new(.methods
@@ -0,0 +1,103 @@
1
+ module Deterministic
2
+ class ProtocolBuilder
3
+
4
+ def initialize(typevar, block)
5
+ @typevar, @block = typevar, block
6
+ @protocol = Class.new
7
+ end
8
+
9
+ def build
10
+ instance_exec(&@block)
11
+ @protocol
12
+ end
13
+
14
+ def method_missing(m, *args)
15
+ [m, args]
16
+ end
17
+
18
+ Signature = Struct.new(:name, :params, :return_type, :block)
19
+
20
+ def fn(signature, &block)
21
+ m = signature.to_a.flatten
22
+ name = m[0]
23
+ return_type = m[-1]
24
+ params = Hash[(m[1..-2][0] || {}).map { |k, v| [k[0], v] }]
25
+
26
+ @protocol.instance_eval {
27
+ define_singleton_method(name) {
28
+ Signature.new(name, params, return_type, block)
29
+ }
30
+ }
31
+
32
+ @protocol.instance_eval {
33
+ if block
34
+ define_method(name) { |*args|
35
+ block.call(args)
36
+ }
37
+ end
38
+ }
39
+ end
40
+ end
41
+
42
+ class InstanceBuilder
43
+ def initialize(protocol, type, block)
44
+ @protocol, @type, @block = protocol, type, block
45
+ @instance = Class.new(@protocol::Protocol)
46
+ end
47
+
48
+ def build
49
+ @instance.class_exec(&@block)
50
+ protocol = @protocol::Protocol
51
+ methods = protocol.methods(false)
52
+ inst_type = @type
53
+
54
+ @instance.instance_exec {
55
+ methods.each { |name|
56
+ if method_defined?(name)
57
+ meth = instance_method(name)
58
+ signature = protocol.send(name)
59
+ params = signature.params
60
+ expect_type = inst_type[signature.return_type]
61
+
62
+ define_method(name) { |*args|
63
+ args.each_with_index { |arg, i|
64
+ name = params.keys[i]
65
+ arg_type = params.fetch(name)
66
+ expect_arg_type = inst_type.fetch(arg_type)
67
+
68
+ raise TypeError, "Expected arg #{name} to be a #{expect_arg_type}, got #<#{arg.class}: #{arg.inspect}>" unless arg.is_a? expect_arg_type
69
+ }
70
+
71
+ result = meth.bind(self).call(*args)
72
+ raise TypeError, "Expected #{name}(#{args.join(', ')}) to return a #{expect_type}, got #<#{result.class}: #{result.inspect}>" unless result.is_a? expect_type
73
+ result
74
+ }
75
+ end
76
+ }
77
+ }
78
+
79
+ missing = methods.detect { |m| !@instance.instance_methods(false).include?(m) }
80
+
81
+ raise NotImplementedError, "`#{missing}` has no default implementation for #{@protocol} #{@type.to_s}" unless missing.nil?
82
+
83
+ @instance
84
+ end
85
+ end
86
+
87
+ module_function
88
+ def protocol(typevar, &block)
89
+ protocol = ProtocolBuilder.new(typevar, block).build
90
+ p_module = block.binding.eval('self')
91
+ p_module.const_set(:Protocol, protocol)
92
+ end
93
+
94
+ def instance(protocol, type, &block)
95
+ InstanceBuilder.new(protocol, type, block).build
96
+ end
97
+
98
+ module Protocol
99
+ def const_missing(c)
100
+ c
101
+ end
102
+ end
103
+ end
@@ -1,122 +1,80 @@
1
- module Deterministic
2
- # Abstract parent of Success and Failure
3
- class Result
4
- include Monad
5
1
 
6
- module PatternMatching
7
- include Deterministic::PatternMatching
8
- class Match
9
- include Deterministic::PatternMatching::Match
2
+ module Deterministic
3
+ Result = Deterministic::enum {
4
+ Success(:s)
5
+ Failure(:f)
6
+ }
10
7
 
11
- %w[Success Failure Result].each do |s|
12
- define_method s.downcase.to_sym do |value=nil, &block|
13
- klas = klas = self.class.module_eval(s)
14
- push(klas, value, block)
15
- end
16
- end
17
- end
8
+ Deterministic::impl(Result) {
9
+ def map(proc=nil, &block)
10
+ match {
11
+ Success(_) { |s| s.bind(proc || block) }
12
+ Failure(_) { |f| f }
13
+ }
18
14
  end
19
15
 
20
- include PatternMatching
21
-
22
- # This is an abstract class, can't ever instantiate it directly
23
- class << self
24
- protected :new
25
- end
16
+ alias :>> :map
17
+ alias :and_then :map
26
18
 
27
- def success?
28
- is_a? Success
19
+ def map_err(proc=nil, &block)
20
+ match {
21
+ Success(_) { |s| s }
22
+ Failure(_) { |f| f.bind(proc|| block) }
23
+ }
29
24
  end
30
25
 
31
- def failure?
32
- is_a? Failure
33
- end
26
+ alias :or_else :map_err
34
27
 
35
- # `pipe(self: Result(a), op: |Result(a)| -> b) -> Result(a)`
36
- # Executes the block passed, but completely ignores its result. If an error is raised within the block it will **NOT** be catched.
37
28
  def pipe(proc=nil, &block)
38
29
  (proc || block).call(self)
39
30
  self
40
31
  end
41
32
 
42
- alias :** :pipe
43
- alias :<< :pipe
33
+ alias :<< :pipe
44
34
 
45
- # `pipe(self: Result(a), op: |Result(a)| -> b) -> Result(a)`
46
- # Replaces `Success a` with `Result b`. If a `Failure` is passed as argument, it is ignored.
47
- def and(other)
48
- return self if failure?
49
- raise NotMonadError, "Expected #{other.inspect} to be an Result" unless other.is_a? Result
50
- other
51
- end
52
-
53
- # `or(self: Failure(a), other: Result(b)) -> Result(b)`
54
- # Replaces `Failure a` with `Result`. If a `Failure` is passed as argument, it is ignored.
55
- def or(other)
56
- return self if success?
57
- raise NotMonadError, "Expected #{other.inspect} to be an Result" unless other.is_a? Result
58
- return other
35
+ def success?
36
+ is_a? Result::Success
59
37
  end
60
38
 
61
- # `map(self: Success(a), op: |a| -> Result(b)) -> Result(b)`
62
- # Maps a `Success` with the value `a` to another `Result` with the value `b`. It works like `#bind` but only on `Success`.
63
- def map(proc=nil, &block)
64
- return self if failure?
65
- bind(proc || block)
39
+ def failure?
40
+ is_a? Result::Failure
66
41
  end
67
42
 
68
- alias :>> :map
69
- alias :and_then :map
70
-
71
- # `map_err(self: Failure(a), op: |a| -> Result(b)) -> Result(b)`
72
- # Maps a `Failure` with the value `a` to another `Result` with the value `b`. It works like `#bind` but only on `Failure`.
73
- def map_err(proc=nil, &block)
74
- return self if success?
75
- bind(proc || block)
43
+ def or(other)
44
+ raise Deterministic::Monad::NotMonadError, "Expected #{other.inspect} to be a Result" unless other.is_a? Result
45
+ match {
46
+ Success(_) { |s| s }
47
+ Failure(_) { other}
48
+ }
76
49
  end
77
50
 
78
- alias :or_else :map_err
79
-
80
- # `pipe(self: Result(a), op: |Result(a)| -> b) -> Result(a)`
81
- # Executes the block passed, but completely ignores its result. If an error is raised within the block it will **NOT** be catched.
82
- def try(proc=nil, &block)
83
- map(proc, &block)
84
- rescue => err
85
- Result::Failure.new(err)
51
+ def and(other)
52
+ raise Deterministic::Monad::NotMonadError, "Expected #{other.inspect} to be a Result" unless other.is_a? Result
53
+ match {
54
+ Success(_) { other }
55
+ Failure(_) { |f| f }
56
+ }
86
57
  end
87
58
 
88
- # `+(self: Result(a), other: Result(a)) -> Result(a -> a)`
89
- # Add the inner values of two Result
90
59
  def +(other)
91
- raise Deterministic::Monad::NotMonadError, "Expected #{other.inspect} to be an Result" unless other.is_a? Result
60
+ raise Deterministic::Monad::NotMonadError, "Expected #{other.inspect} to be a Result" unless other.is_a? Result
92
61
  match {
93
- success ->(_) { other.success? } { |s| Result::Success.new(s + other.value)}
94
- failure ->(_) { other.failure? } { |f| Result::Failure.new(f + other.value)}
95
- success { other } # implied other.failure?
96
- failure { self } # implied other.success?
62
+ Success(s, where { other.success?} ) { Result::Success.new(s + other.value) }
63
+ Failure(f, where { other.failure?} ) { Result::Failure.new(f + other.value) }
64
+ Success(_) { other } # implied other.failure?
65
+ Failure(_) { |f| f } # implied other.success?
97
66
  }
98
67
  end
68
+ }
69
+ end
99
70
 
100
- alias :>= :try
101
-
102
- class Failure < Result
103
- class << self; public :new; end
104
- end
105
-
106
- class Success < Result
107
- class << self; public :new; end
71
+ module Deterministic
72
+ module Prelude
73
+ module Result
74
+ def Success(s); Deterministic::Result::Success.new(s); end
75
+ def Failure(f); Deterministic::Result::Failure.new(f); end
108
76
  end
109
- end
110
77
 
111
- module_function
112
- def Success(value)
113
- Result::Success.new(value)
78
+ include Result
114
79
  end
115
-
116
- def Failure(value)
117
- Result::Failure.new(value)
118
- end
119
-
120
- Success = Result::Success
121
- Failure = Result::Failure
122
80
  end