monadt 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c19e86f991142006e0e23f98ec59c2a9780db485
4
+ data.tar.gz: 52cc959d8eeb05fa7d617d51278ec9b2aed00467
5
+ SHA512:
6
+ metadata.gz: a06ffff7277f97c3a16652bbb45ed0d0c13a9ad382772a58d372dbdf9b93e2ae45ce9eafdca7612ed29dffc9a5c1513a4be98d2cae09fb128450e2f5142f91d4
7
+ data.tar.gz: a61dab72be3127c89f11d33ad49af0094838073554719fd2f10de78a6408120302c9818c5843ec71b94c379f43d0348e962dbd5a80591cd545b3e6d6bea23b93
data/lib/monadt/adt.rb ADDED
@@ -0,0 +1,33 @@
1
+ AdtPattern = Struct.new :klass, :lambda
2
+
3
+ def data(*fields)
4
+ base = if fields.size > 0
5
+ Struct.new(*fields)
6
+ else
7
+ Object
8
+ end
9
+ Class.new(base)
10
+ end
11
+
12
+ Default = data
13
+
14
+ module Adt
15
+ def match(o, *cases)
16
+ m = cases.find do |tpl|
17
+ tpl.klass == o.class || tpl.klass == Default
18
+ end
19
+ params =
20
+ if m.lambda.arity > 0
21
+ o.values.take(m.lambda.arity)
22
+ else
23
+ []
24
+ end
25
+ m.lambda.call(*params)
26
+ end
27
+
28
+ def with(klass, prc=nil, &blk)
29
+ AdtPattern.new klass, prc || blk
30
+ end
31
+ end
32
+
33
+
@@ -0,0 +1,35 @@
1
+ require 'monadt/adt'
2
+
3
+ module Monadt
4
+ module Either
5
+ Left = data :left
6
+ Right = data :right
7
+ end
8
+ class Either::Left
9
+ def is_left?() true end
10
+ def is_right?() false end
11
+ def to_s() "Left(#{left})" end
12
+ end
13
+ class Either::Right
14
+ def is_left?() false end
15
+ def is_right?() true end
16
+ def to_s() "Right(#{right})" end
17
+ end
18
+
19
+ class EitherM
20
+ class << self
21
+ include Adt
22
+
23
+ def bind(m, &blk)
24
+ match(m,
25
+ with(Either::Left) { |v| m },
26
+ with(Either::Right) { |v| blk.call(v) })
27
+ end
28
+
29
+ def return(a)
30
+ Either::Right.new a
31
+ end
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,17 @@
1
+ require_relative 'maybe'
2
+ require_relative 'either'
3
+ require_relative 'state'
4
+ require_relative 'reader_state_either'
5
+
6
+ class ListM
7
+ class << self
8
+ def bind(m, &blk)
9
+ m.flat_map(&blk)
10
+ end
11
+
12
+ def return(a)
13
+ [a]
14
+ end
15
+ end
16
+ end
17
+
@@ -0,0 +1,51 @@
1
+ require 'monadt/adt'
2
+
3
+ module Monadt
4
+ module Maybe
5
+ Just = data :value
6
+ Nothing = data
7
+ end
8
+
9
+ class Maybe::Just
10
+ def is_nothing?() false end
11
+ def to_s() "Just #{value}" end
12
+ end
13
+ class Maybe::Nothing
14
+ def is_nothing?() true end
15
+ def to_s() "Nothing" end
16
+ end
17
+
18
+ class MaybeM
19
+ class << self
20
+ include Adt
21
+
22
+ def bind(m, &blk)
23
+ match(m,
24
+ with(Maybe::Just) { |v| blk.call(v) },
25
+ with(Maybe::Nothing) { m })
26
+ end
27
+
28
+ def return(a)
29
+ Maybe::Just.new a
30
+ end
31
+ end
32
+ end
33
+
34
+ # like Maybe but nil / not-nill
35
+ class PresentM
36
+ class << self
37
+ def bind(m, &blk)
38
+ if m.nil?
39
+ nil
40
+ else
41
+ blk.call(m)
42
+ end
43
+ end
44
+
45
+ def return(a)
46
+ a
47
+ end
48
+ end
49
+ end
50
+ end
51
+
@@ -0,0 +1,43 @@
1
+ module Monadt
2
+ module Internal
3
+ class MonadObj
4
+ def initialize(klass, yielder)
5
+ @yielder = yielder
6
+ @klass = klass
7
+ end
8
+
9
+ def return(val)
10
+ @klass.return val
11
+ end
12
+
13
+ def bind(val)
14
+ @yielder.yield val
15
+ end
16
+ end
17
+ end
18
+
19
+ class Monad
20
+ class << self
21
+ def do_m(klass, &blk)
22
+ e = Enumerator.new do |y|
23
+ m_obj = Internal::MonadObj.new klass, y
24
+ blk.call(m_obj)
25
+ end
26
+ do_m_recur(klass, e)
27
+ end
28
+
29
+ def do_m_recur(klass, e)
30
+ begin
31
+ ma = e.next
32
+ rescue StopIteration => ex
33
+ return ex.result
34
+ end
35
+ klass.bind(ma) do |a|
36
+ e.feed a
37
+ do_m_recur(klass, e)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+
@@ -0,0 +1,16 @@
1
+ module Monadt
2
+ class ReaderM
3
+ class << self
4
+ def bind(m, &blk)
5
+ ->(e) {
6
+ v = m.(e)
7
+ blk.call(e, v)
8
+ }
9
+ end
10
+
11
+ def return(val)
12
+ ->(e) { val }
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,24 @@
1
+ require 'monadt/adt'
2
+
3
+ module Monadt
4
+ class ReaderStateEitherM
5
+ class << self
6
+ include Adt
7
+
8
+ def bind(m, &blk)
9
+ ->(e, s) {
10
+ c, s2 = m.(e, s)
11
+ match(c,
12
+ with(Either::Left) {|v| [c, s2]},
13
+ with(Either::Right) {|v|
14
+ blk.call(v).(e, s2)
15
+ })
16
+ }
17
+ end
18
+
19
+ def return(val)
20
+ ->(e, s) { [EitherM.return(val), s] }
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,16 @@
1
+ module Monadt
2
+ class StateM
3
+ class << self
4
+ def bind(m, &blk)
5
+ ->(st) {
6
+ v, s = m.(st)
7
+ blk.call(v).(s)
8
+ }
9
+ end
10
+
11
+ def return(val)
12
+ ->(st) { [val, st] }
13
+ end
14
+ end
15
+ end
16
+ end
data/lib/monadt.rb ADDED
@@ -0,0 +1,49 @@
1
+ require 'monadt/maybe'
2
+ require 'monadt/either'
3
+ require 'monadt/state'
4
+ require 'monadt/reader_state_either'
5
+ require 'monadt/monad'
6
+
7
+ module Monadt
8
+ class Monad
9
+ class << self
10
+ def maybe(&blk)
11
+ do_m(MaybeM, &blk)
12
+ end
13
+
14
+ def present(&blk)
15
+ do_m(PresentM, &blk)
16
+ end
17
+
18
+ def either(&blk)
19
+ do_m(EitherM, &blk)
20
+ end
21
+
22
+ def state(&blk)
23
+ do_m(StateM, &blk)
24
+ end
25
+
26
+ def run_state(initial_state, &blk)
27
+ prc = state(&blk)
28
+ prc.(initial_state).first
29
+ end
30
+
31
+ def reader(&blk)
32
+ do_m(ReaderM, &blk)
33
+ end
34
+
35
+ def run_reader(env, &blk)
36
+ reader(&blk).(env)
37
+ end
38
+
39
+ def reader_state_choice(&blk)
40
+ do_m(ReaderStateEitherM, &blk)
41
+ end
42
+
43
+ def run_reader_state_choice(env, initial_state, &blk)
44
+ reader_state_choice(&blk).(env, initial_state).first
45
+ end
46
+ end
47
+ end
48
+ end
49
+
metadata ADDED
@@ -0,0 +1,53 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: monadt
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Will Pleasant-Ryan
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-03-25 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Functions to create ADTs and do pattern matching, as well as Enumerator
14
+ based Monad computation
15
+ email: will.ryan@atomicobject.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - lib/monadt.rb
21
+ - lib/monadt/adt.rb
22
+ - lib/monadt/either.rb
23
+ - lib/monadt/list.rb
24
+ - lib/monadt/maybe.rb
25
+ - lib/monadt/monad.rb
26
+ - lib/monadt/reader.rb
27
+ - lib/monadt/reader_state_either.rb
28
+ - lib/monadt/state.rb
29
+ homepage: https://github.com/atomicobject/monadt
30
+ licenses:
31
+ - Apache
32
+ metadata: {}
33
+ post_install_message:
34
+ rdoc_options: []
35
+ require_paths:
36
+ - lib
37
+ required_ruby_version: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ required_rubygems_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ requirements: []
48
+ rubyforge_project:
49
+ rubygems_version: 2.4.5
50
+ signing_key:
51
+ specification_version: 4
52
+ summary: ADTs and Monads in Ruby
53
+ test_files: []