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