fr 0.9.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +0 -0
- data/Rakefile +81 -0
- data/lib/fr.rb +44 -0
- data/lib/fr/applicative.rb +0 -0
- data/lib/fr/array.rb +168 -0
- data/lib/fr/boolean.rb +15 -0
- data/lib/fr/either.rb +94 -0
- data/lib/fr/errors.rb +5 -0
- data/lib/fr/errors/zipper_error.rb +8 -0
- data/lib/fr/functor.rb +11 -0
- data/lib/fr/functor/array.rb +15 -0
- data/lib/fr/functor/either.rb +17 -0
- data/lib/fr/functor/maybe.rb +17 -0
- data/lib/fr/maybe.rb +76 -0
- data/lib/fr/monad.rb +236 -0
- data/lib/fr/monad/array.rb +21 -0
- data/lib/fr/monad/either.rb +18 -0
- data/lib/fr/monad/maybe.rb +24 -0
- data/lib/fr/monad/random.rb +50 -0
- data/lib/fr/monad/reader.rb +45 -0
- data/lib/fr/monad/state.rb +62 -0
- data/lib/fr/monad/writer.rb +49 -0
- data/lib/fr/monoid.rb +52 -0
- data/lib/fr/monoid/array.rb +13 -0
- data/lib/fr/monoid/boolean.rb +23 -0
- data/lib/fr/monoid/either.rb +13 -0
- data/lib/fr/monoid/maybe.rb +15 -0
- data/lib/fr/monoid/numeric.rb +31 -0
- data/lib/fr/monoid/string.rb +11 -0
- data/lib/fr/numeric.rb +1 -0
- data/lib/fr/object.rb +45 -0
- data/lib/fr/string.rb +1 -0
- data/lib/fr/szipper.rb +36 -0
- data/lib/fr/szipper/abstract_cursor.rb +63 -0
- data/lib/fr/szipper/edited_cursor.rb +62 -0
- data/lib/fr/szipper/enumerable.rb +266 -0
- data/lib/fr/szipper/head_cursor.rb +71 -0
- data/lib/fr/szipper/head_prev_cursor.rb +112 -0
- data/lib/fr/szipper/memoized_cursor.rb +59 -0
- data/lib/fr/szipper/node.rb +67 -0
- data/lib/fr/szipper/tail_next_cursor.rb +68 -0
- data/lib/fr/thunk.rb +73 -0
- data/lib/fr/tzipper.rb +38 -0
- data/lib/fr/tzipper/abstract_cursor.rb +351 -0
- data/lib/fr/tzipper/dangling_cursor.rb +107 -0
- data/lib/fr/tzipper/edited_cursor.rb +161 -0
- data/lib/fr/tzipper/memoized_cursor.rb +129 -0
- data/lib/fr/tzipper/node.rb +57 -0
- data/lib/fr/tzipper/path.rb +125 -0
- data/lib/fr/tzipper/root_cursor.rb +124 -0
- data/lib/fr/unfold.rb +93 -0
- data/spec/examples/array.example +0 -0
- data/spec/examples/monads/array.example +0 -0
- data/spec/examples/monads/maybe.example +0 -0
- data/spec/examples/monads/random.example +0 -0
- data/spec/examples/monads/state.example +0 -0
- data/spec/examples/szipper.example +652 -0
- data/spec/examples/thunk.example +0 -0
- data/spec/examples/tzipper.example +0 -0
- data/spec/examples/unfold.example +90 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/support/szipper/model.rb +225 -0
- data/spec/support/szipper/nodel.rb +144 -0
- 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,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
|
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
|