fr 0.9.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.
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