fr 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. data/README.md +0 -0
  2. data/Rakefile +81 -0
  3. data/lib/fr.rb +44 -0
  4. data/lib/fr/applicative.rb +0 -0
  5. data/lib/fr/array.rb +168 -0
  6. data/lib/fr/boolean.rb +15 -0
  7. data/lib/fr/either.rb +94 -0
  8. data/lib/fr/errors.rb +5 -0
  9. data/lib/fr/errors/zipper_error.rb +8 -0
  10. data/lib/fr/functor.rb +11 -0
  11. data/lib/fr/functor/array.rb +15 -0
  12. data/lib/fr/functor/either.rb +17 -0
  13. data/lib/fr/functor/maybe.rb +17 -0
  14. data/lib/fr/maybe.rb +76 -0
  15. data/lib/fr/monad.rb +236 -0
  16. data/lib/fr/monad/array.rb +21 -0
  17. data/lib/fr/monad/either.rb +18 -0
  18. data/lib/fr/monad/maybe.rb +24 -0
  19. data/lib/fr/monad/random.rb +50 -0
  20. data/lib/fr/monad/reader.rb +45 -0
  21. data/lib/fr/monad/state.rb +62 -0
  22. data/lib/fr/monad/writer.rb +49 -0
  23. data/lib/fr/monoid.rb +52 -0
  24. data/lib/fr/monoid/array.rb +13 -0
  25. data/lib/fr/monoid/boolean.rb +23 -0
  26. data/lib/fr/monoid/either.rb +13 -0
  27. data/lib/fr/monoid/maybe.rb +15 -0
  28. data/lib/fr/monoid/numeric.rb +31 -0
  29. data/lib/fr/monoid/string.rb +11 -0
  30. data/lib/fr/numeric.rb +1 -0
  31. data/lib/fr/object.rb +45 -0
  32. data/lib/fr/string.rb +1 -0
  33. data/lib/fr/szipper.rb +36 -0
  34. data/lib/fr/szipper/abstract_cursor.rb +63 -0
  35. data/lib/fr/szipper/edited_cursor.rb +62 -0
  36. data/lib/fr/szipper/enumerable.rb +266 -0
  37. data/lib/fr/szipper/head_cursor.rb +71 -0
  38. data/lib/fr/szipper/head_prev_cursor.rb +112 -0
  39. data/lib/fr/szipper/memoized_cursor.rb +59 -0
  40. data/lib/fr/szipper/node.rb +67 -0
  41. data/lib/fr/szipper/tail_next_cursor.rb +68 -0
  42. data/lib/fr/thunk.rb +73 -0
  43. data/lib/fr/tzipper.rb +38 -0
  44. data/lib/fr/tzipper/abstract_cursor.rb +351 -0
  45. data/lib/fr/tzipper/dangling_cursor.rb +107 -0
  46. data/lib/fr/tzipper/edited_cursor.rb +161 -0
  47. data/lib/fr/tzipper/memoized_cursor.rb +129 -0
  48. data/lib/fr/tzipper/node.rb +57 -0
  49. data/lib/fr/tzipper/path.rb +125 -0
  50. data/lib/fr/tzipper/root_cursor.rb +124 -0
  51. data/lib/fr/unfold.rb +93 -0
  52. data/spec/examples/array.example +0 -0
  53. data/spec/examples/monads/array.example +0 -0
  54. data/spec/examples/monads/maybe.example +0 -0
  55. data/spec/examples/monads/random.example +0 -0
  56. data/spec/examples/monads/state.example +0 -0
  57. data/spec/examples/szipper.example +652 -0
  58. data/spec/examples/thunk.example +0 -0
  59. data/spec/examples/tzipper.example +0 -0
  60. data/spec/examples/unfold.example +90 -0
  61. data/spec/spec_helper.rb +23 -0
  62. data/spec/support/szipper/model.rb +225 -0
  63. data/spec/support/szipper/nodel.rb +144 -0
  64. metadata +116 -0
@@ -0,0 +1,45 @@
1
+ module Fr
2
+ module Reader
3
+ end
4
+
5
+ class << Reader
6
+ include Monad
7
+
8
+ # Evaluators
9
+ #############################################
10
+
11
+ def run(computation, state)
12
+ computation.call(state)
13
+ end
14
+
15
+ def eval(computation, state)
16
+ computation.call(state)[:value]
17
+ end
18
+
19
+ # Combinators
20
+ #############################################
21
+
22
+ def unit(value)
23
+ lambda do |state|
24
+ Hash[state: state, value: value]
25
+ end
26
+ end
27
+
28
+ def bind(f, &g)
29
+ lambda do |state|
30
+ tuple = f.call(state)
31
+ g.call(tuple[:value])
32
+ .call(tuple[:state])
33
+ end
34
+ end
35
+
36
+ # Actions
37
+ #############################################
38
+
39
+ def ask
40
+ lambda do |state|
41
+ Hash[state: state, value: state]
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,62 @@
1
+ module Fr
2
+
3
+ module State
4
+ end
5
+
6
+ class << State
7
+ include Monad
8
+
9
+ # Evaluators
10
+ #############################################
11
+
12
+ def run(computation, state)
13
+ computation.call(state)
14
+ end
15
+
16
+ def eval(computation, state)
17
+ computation.call(state)[:value]
18
+ end
19
+
20
+ def exec(computation, state)
21
+ computation.call(state)[:state]
22
+ end
23
+
24
+ # Combinators
25
+ #############################################
26
+
27
+ def unit(value)
28
+ lambda do |state|
29
+ Hash[state: state, value: value]
30
+ end
31
+ end
32
+
33
+ def bind(f, &g)
34
+ lambda do |state|
35
+ tuple = f.call(state)
36
+ g.call(tuple[:value]).call(tuple[:state])
37
+ end
38
+ end
39
+
40
+ # Actions
41
+ #############################################
42
+
43
+ def put(n)
44
+ lambda do |state|
45
+ Hash[state: n, value: nil]
46
+ end
47
+ end
48
+
49
+ def get
50
+ lambda do |state|
51
+ Hash[state: state, value: state]
52
+ end
53
+ end
54
+
55
+ def update
56
+ lambda do |state|
57
+ Hash[state: yield(state), value: nil]
58
+ end
59
+ end
60
+ end
61
+
62
+ end
@@ -0,0 +1,49 @@
1
+ module Fr
2
+ module Writer
3
+ end
4
+
5
+ class << Writer
6
+ include Monad
7
+
8
+ # Evaluators
9
+ #############################################
10
+
11
+ def run(computation, monoid)
12
+ computation.call(monoid.zero, monoid)
13
+ end
14
+
15
+ def eval(computation, monoid)
16
+ computation.call(monoid.zero, monoid)[:value]
17
+ end
18
+
19
+ def exec(computation, monoid)
20
+ computation.call(monoid.zero, monoid)[:state]
21
+ end
22
+
23
+ # Combinators
24
+ #############################################
25
+
26
+ def unit(value)
27
+ lambda do |state, monoid|
28
+ Hash[state: state, monoid: monoid, value: value]
29
+ end
30
+ end
31
+
32
+ def bind(f, &g)
33
+ lambda do |state, monoid|
34
+ tuple = f.call(state, monoid)
35
+ g.call(tuple[:value])
36
+ .call(tuple[:state], tuple[:monoid])
37
+ end
38
+ end
39
+
40
+ # Actions
41
+ #############################################
42
+
43
+ def tell(write)
44
+ lambda do |state, monoid|
45
+ Hash[state: monoid.plus(state, write), monoid: monoid, value: nil]
46
+ end
47
+ end
48
+ end
49
+ end
data/lib/fr/monoid.rb ADDED
@@ -0,0 +1,52 @@
1
+ module Fr
2
+ module Monoid
3
+ # zero: m
4
+ # plus: m -> m -> m
5
+
6
+ # :: Monoid m => [m] -> m
7
+ #
8
+ # >> [m.unit(1), m.unit(2)].sum
9
+ # => m.plus(m.plus(m.zero, m.unit(1)), m.unit(2))
10
+ #
11
+ def sum(ms)
12
+ ms.inject(zero){|sum,m| plus(sum, m) }
13
+ end
14
+
15
+ # :: Monad m, Monoid m => Bool -> m ()
16
+ #
17
+ # >> m.guard(false)
18
+ # => m.zero
19
+ #
20
+ # Not supported:
21
+ # >> true.guard(m.unit(x))
22
+ # => m.unit(x)
23
+ #
24
+ def guard(bool)
25
+ bool ?
26
+ unit(nil) : zero
27
+ end
28
+
29
+ # :: Monad m, Monoid m => (a -> Bool) -> m a -> m a
30
+ #
31
+ # >> m.filter(lambda{|x| x.odd? }).call(m.unit(10))
32
+ # => m.zero
33
+ #
34
+ # Not supported:
35
+ # >> m.unit(10).filter{|x| x.odd? }
36
+ # => m.zero
37
+ #
38
+ def filter(f, ma = nil)
39
+ rest =
40
+ lambda do |ma|
41
+ bind(ma) do |a|
42
+ f.call(a) ?
43
+ unit(a) : zero
44
+ end
45
+ end
46
+
47
+ (ma.nil?) ?
48
+ rest : rest.call(ma)
49
+ end
50
+
51
+ end
52
+ end
@@ -0,0 +1,13 @@
1
+ class << Array
2
+ include Fr::Monoid
3
+
4
+ # :: Array a
5
+ def zero
6
+ []
7
+ end
8
+
9
+ # :: Array a -> Array a -> Array a
10
+ def plus(a, b)
11
+ a + b
12
+ end
13
+ end
@@ -0,0 +1,23 @@
1
+ class Conjunctive
2
+ class << self
3
+ def zero
4
+ true
5
+ end
6
+
7
+ def sum(a, b)
8
+ a && b
9
+ end
10
+ end
11
+ end
12
+
13
+ class Disjunctive
14
+ class << self
15
+ def zero
16
+ false
17
+ end
18
+
19
+ def sum(a, b)
20
+ a || b
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,13 @@
1
+ module Fr
2
+ class << Either
3
+ include Monoid
4
+
5
+ def zero
6
+ Either::Left.new(nil)
7
+ end
8
+
9
+ def plus(a, b)
10
+ (a.left?) ? b : a
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,15 @@
1
+ module Fr
2
+ class << Maybe
3
+ include Monoid
4
+
5
+ # :: Maybe a
6
+ def zero
7
+ Maybe::None
8
+ end
9
+
10
+ # :: Maybe a -> Maybe a -> Maybe a
11
+ def plus(a, b)
12
+ (a.none?) ? b : a
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,31 @@
1
+ class Additive
2
+ class << self
3
+ include Fr::Monoid
4
+
5
+ # :: Numeric a => a
6
+ def zero
7
+ 0
8
+ end
9
+
10
+ # :: Numeric a => a -> a -> a
11
+ def plus(a, b)
12
+ a + b
13
+ end
14
+ end
15
+ end
16
+
17
+ class Multiplicative
18
+ class << self
19
+ include Fr::Monoid
20
+
21
+ # :: Numeric a => a
22
+ def zero
23
+ 1
24
+ end
25
+
26
+ # :: Numeric a => a -> a -> a
27
+ def plus(a, b)
28
+ a * b
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,11 @@
1
+ class << String
2
+ include Fr::Monoid
3
+
4
+ def zero
5
+ ""
6
+ end
7
+
8
+ def plus(a, b)
9
+ a + b
10
+ end
11
+ end
data/lib/fr/numeric.rb ADDED
@@ -0,0 +1 @@
1
+ require "fr/monoid/numeric"
data/lib/fr/object.rb ADDED
@@ -0,0 +1,45 @@
1
+ class Object
2
+
3
+ # @group List Constructors
4
+ #############################################################################
5
+
6
+ # Prepend the item to the front of a new list
7
+ #
8
+ # @example
9
+ # 1.cons #=> [1]
10
+ # 1.cons(2.cons) #=> [1, 2]
11
+ # 1.cons([0, 0, 0]) #=> [1, 0, 0, 0]
12
+ #
13
+ # @return [Array]
14
+ def cons(tail = [])
15
+ [self] + tail
16
+ end
17
+
18
+ # Append the item to rear of a new list
19
+ #
20
+ # @example
21
+ # 1.snoc #=> [1]
22
+ # 1.snoc(2.snoc) #=> [2, 1]
23
+ # 1.snoc([0, 0, 0]) #=> [0, 0, 0, 1]
24
+ #
25
+ # @return [Array]
26
+ def snoc(init = [])
27
+ init + [self]
28
+ end
29
+
30
+ # @group Combinators
31
+ #############################################################################
32
+
33
+ # Yields `self` to a block argument
34
+ #
35
+ # @example
36
+ # nil.bind{|a| a.nil? } #=> true
37
+ # 100.bind{|a| a.nil? } #=> false
38
+ #
39
+ def bind
40
+ yield self
41
+ end
42
+
43
+ # @endgroup
44
+ #############################################################################
45
+ end
data/lib/fr/string.rb ADDED
@@ -0,0 +1 @@
1
+ require "fr/monoid/string.rb"
data/lib/fr/szipper.rb ADDED
@@ -0,0 +1,36 @@
1
+ module Fr
2
+ module SZipper
3
+ autoload :AbstractCursor, "fr/szipper/abstract_cursor"
4
+ autoload :EditedCursor, "fr/szipper/edited_cursor"
5
+ autoload :MemoizedCursor, "fr/szipper/memoized_cursor"
6
+ autoload :HeadPrevCursor, "fr/szipper/head_prev_cursor"
7
+ autoload :TailNextCursor, "fr/szipper/tail_next_cursor"
8
+ autoload :HeadCursor, "fr/szipper/head_cursor"
9
+ autoload :Enumerable, "fr/szipper/enumerable"
10
+ autoload :Node, "fr/szipper/node"
11
+ end
12
+
13
+ class << SZipper
14
+
15
+ # Constructors a zipper for traversing the given tree or
16
+ # subtree. The `node` value can be any structure where
17
+ #
18
+ # node#next
19
+ # the next node
20
+ #
21
+ # node#last?
22
+ # true if no nodes are allowed to be inserted
23
+ #
24
+ # node#copy(next: ...)
25
+ # creates a new node with the given next node
26
+ #
27
+ def build(node = nil)
28
+ if node
29
+ Fr::SZipper::HeadCursor.new(node)
30
+ else
31
+ Fr::SZipper::HeadPrevCursor.new(nil)
32
+ end
33
+ end
34
+ end
35
+
36
+ end
@@ -0,0 +1,63 @@
1
+ module Fr
2
+ module SZipper
3
+
4
+ class AbstractCursor
5
+ # : node
6
+ # : prev(phantom = false)
7
+ # : next(phantom = false)
8
+ # : head?
9
+ # : tail?
10
+ # : empty?
11
+ # : head
12
+ # : tail
13
+ # : position
14
+ # : append(node)
15
+ # : prepend(node)
16
+ # : update(node = nil) {|node| node }
17
+ # : replace(node)
18
+ # : delete
19
+ # : truncate
20
+ include Enumerable
21
+
22
+ def empty?
23
+ false
24
+ end
25
+
26
+ def next(phantom = false)
27
+ @__next ||= begin
28
+ if tail?
29
+ if phantom
30
+ TailNextCursor.new(self)
31
+ else
32
+ raise Errors::ZipperError,
33
+ "tail node has no next"
34
+ end
35
+ else
36
+ MemoizedCursor.new(node.next, self)
37
+ end
38
+ end
39
+ end
40
+
41
+ def head
42
+ cursor = self
43
+ cursor = cursor.prev until cursor.head?
44
+ cursor
45
+ end
46
+
47
+ def tail
48
+ cursor = self
49
+ cursor = cursor.next until cursor.tail?
50
+ cursor
51
+ end
52
+
53
+ def position
54
+ @prev.position + 1
55
+ end
56
+
57
+ def truncate
58
+ update(node.copy(next: nil))
59
+ end
60
+ end
61
+
62
+ end
63
+ end