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
data/README.md ADDED
File without changes
data/Rakefile ADDED
@@ -0,0 +1,81 @@
1
+ require "pathname"
2
+ abspath = Pathname.new(File.dirname(__FILE__)).expand_path
3
+ relpath = abspath.relative_path_from(Pathname.pwd)
4
+
5
+ begin
6
+ require "rubygems"
7
+ require "bundler/setup"
8
+ rescue LoadError
9
+ warn "couldn't load bundler:"
10
+ warn " #{$!}"
11
+ end
12
+
13
+ task :console do
14
+ exec *%w(irb -I lib -r fr)
15
+ end
16
+
17
+ begin
18
+ require "rspec/core/rake_task"
19
+ RSpec::Core::RakeTask.new do |t|
20
+ t.verbose = false
21
+ t.pattern = "#{relpath}/spec/examples/**/*.example"
22
+
23
+ t.rspec_opts = %w(--color --format p)
24
+ t.rspec_opts << "-I#{abspath}/spec"
25
+ end
26
+ rescue LoadError
27
+ task :spec do
28
+ warn "couldn't load rspec"
29
+ warn " #{$!}"
30
+ exit 1
31
+ end
32
+ end
33
+
34
+ begin
35
+ require "rcov"
36
+ begin
37
+ require "rspec/core/rake_task"
38
+ RSpec::Core::RakeTask.new(:rcov) do |t|
39
+ t.rcov = true
40
+ t.rcov_opts = "--exclude spec/,gems/"
41
+
42
+ t.verbose = false
43
+ t.pattern = "#{relpath}/spec/examples/**/*.example"
44
+
45
+ t.rspec_opts = %w(--color --format p)
46
+ t.rspec_opts << "-I#{abspath}/spec"
47
+ end
48
+ rescue LoadError
49
+ task :rcov do
50
+ warn "couldn't load rspec"
51
+ warn " #{$!}"
52
+ exit 1
53
+ end
54
+ end
55
+ rescue LoadError
56
+ task :rcov do
57
+ warn "couldn't load rcov:"
58
+ warn " #{$!}"
59
+ exit 1
60
+ end
61
+ end
62
+
63
+ begin
64
+ require "yard"
65
+
66
+ # Note options are loaded from .yardopts
67
+ YARD::Rake::YardocTask.new(:yard => :clobber_yard)
68
+
69
+ task :clobber_yard do
70
+ rm_rf "#{relpath}/doc/generated"
71
+ mkdir_p "#{relpath}/doc/generated/images"
72
+ end
73
+ rescue LoadError
74
+ task :yard do
75
+ warn "couldn't load yard:"
76
+ warn " #{$!}"
77
+ exit 1
78
+ end
79
+ end
80
+
81
+ task :default => :spec
data/lib/fr.rb ADDED
@@ -0,0 +1,44 @@
1
+ module Fr
2
+ autoload :Errors, "fr/errors"
3
+ autoload :Maybe, "fr/maybe"
4
+ autoload :Either, "fr/either"
5
+ autoload :Functor, "fr/functor"
6
+ autoload :Monad, "fr/monad"
7
+ autoload :Monoid, "fr/monoid"
8
+ autoload :Additive, "fr/monoid/numeric"
9
+ autoload :Multiplicitive, "fr/monoid/numeric"
10
+ autoload :Random, "fr/monad/random"
11
+ autoload :State, "fr/monad/state"
12
+ autoload :Reader, "fr/monad/reader"
13
+ autoload :Writer, "fr/monad/writer"
14
+ autoload :SZipper, "fr/szipper"
15
+ autoload :TZipper, "fr/tzipper"
16
+ autoload :Thunk, "fr/thunk"
17
+
18
+ def self.thunk(&block)
19
+ Thunk.new(block)
20
+ end
21
+
22
+ def self.some(value)
23
+ Fr::Maybe::Some.new(value)
24
+ end
25
+
26
+ def self.none
27
+ Fr::Maybe::None
28
+ end
29
+
30
+ def self.left(value)
31
+ Fr::Either::Left.new(value)
32
+ end
33
+
34
+ def self.right(value)
35
+ Fr::Either::Right.new(value)
36
+ end
37
+ end
38
+
39
+ require "fr/array"
40
+ require "fr/boolean"
41
+ require "fr/object"
42
+ require "fr/unfold"
43
+ require "fr/string"
44
+ require "fr/numeric"
File without changes
data/lib/fr/array.rb ADDED
@@ -0,0 +1,168 @@
1
+ class Array
2
+
3
+ # Return the first item. Raises an `IndexError` if the Array is `empty?`.
4
+ #
5
+ # @example
6
+ # [1, 2, 3].head #=> 1
7
+ #
8
+ def head
9
+ raise IndexError, "head of empty list" if empty?
10
+ x, = self
11
+ x
12
+ end
13
+
14
+ # True if `#at` is defined for the given `n`
15
+ #
16
+ # @example
17
+ # [1, 2, 3].defined_at?(0) #=> true
18
+ # [].defined_at?(0) #=> false
19
+ #
20
+ def defined_at?(n)
21
+ n < length and -n <= length
22
+ end
23
+
24
+ # @group Selection
25
+ #############################################################################
26
+
27
+ # Selects all elements except the first.
28
+ #
29
+ # @example
30
+ # [1, 2, 3].tail #=> [2, 3]
31
+ # [1].tail #=> []
32
+ # [].tail #=> []
33
+ #
34
+ # @return [Array]
35
+ def tail
36
+ _, *xs = self
37
+ xs
38
+ end
39
+
40
+ # Selects all elements except the last `n` ones.
41
+ #
42
+ # @example
43
+ # [1, 2, 3].init #=> [1, 2]
44
+ # [1, 2, 3].init(2) #=> [1]
45
+ # [].tail #=> []
46
+ #
47
+ # @return [Array]
48
+ def init(n = 1)
49
+ raise ArgumentError, "n cannot be negative" if n < 0
50
+ slice(0..-(n + 1)) or []
51
+ end
52
+
53
+ # Select all elements except the first `n` ones.
54
+ #
55
+ # @example
56
+ # [1, 2, 3].drop(1) #=> [2, 3]
57
+ # [1, 3, 3].drop(2) #=> [3]
58
+ # [].drop(10) #=> []
59
+ #
60
+ # @return [Array]
61
+ def drop(n)
62
+ raise ArgumentError, "n cannot be negative" if n < 0
63
+ slice(n..-1) or []
64
+ end
65
+
66
+ # Select the first `n` elements.
67
+ #
68
+ # @example
69
+ # [1, 2, 3].take(2) #=> [1, 2]
70
+ # [1, 2, 3].take(0) #=> []
71
+ #
72
+ # @return [Array]
73
+ def take(n)
74
+ raise ArgumentError, "n cannot be negative" if n < 0
75
+ slice(0, n) or []
76
+ end
77
+
78
+ # Split the array in two at the given position.
79
+ #
80
+ # @example
81
+ # [1, 2, 3].split_at(2) #=> [[1,2], [3]]
82
+ # [1, 2, 3].split_at(0) #=> [[], [1,2,3]]
83
+ #
84
+ # @return [(Array, Array)]
85
+ def split_at(n)
86
+ n = length + n if n < 0
87
+ return take(n), drop(n)
88
+ end
89
+
90
+ # @endgroup
91
+ #############################################################################
92
+
93
+ # @group Filtering
94
+ #############################################################################
95
+
96
+ # Drops the longest prefix of elements that satisfy the predicate.
97
+ #
98
+ # @return [Array]
99
+ def drop_while(&block)
100
+ # This is in tail call form
101
+ if not empty? and yield(head)
102
+ tail.drop_while(&block)
103
+ else
104
+ self
105
+ end
106
+ end
107
+
108
+ # Drops the longest prefix of elements that do not satisfy the predicate.
109
+ #
110
+ # @return [Array]
111
+ def drop_until(&block)
112
+ # This is in tail call form
113
+ unless empty? or yield(head)
114
+ tail.drop_until(&block)
115
+ else
116
+ self
117
+ end
118
+ end
119
+
120
+ # Takes the longest prefix of elements that satisfy the predicate.
121
+ #
122
+ # @return [Array]
123
+ def take_while(accumulator = [], &block)
124
+ # This is in tail call form
125
+ if not empty? and yield(head)
126
+ tail.take_while(head.snoc(accumulator), &block)
127
+ else
128
+ accumulator
129
+ end
130
+ end
131
+
132
+ # Takes the longest prefix of elements that do not satisfy the predicate.
133
+ #
134
+ # @return [Array]
135
+ def take_until(accumulator = [], &block)
136
+ # This is in tail call form
137
+ unless empty? or yield(head)
138
+ tail.take_until(head.snoc(accumulator), &block)
139
+ else
140
+ accumulator
141
+ end
142
+ end
143
+
144
+ # Splits the array into prefix/suffix pair according to the predicate.
145
+ #
146
+ # @return [(Array, Array)]
147
+ def split_until(&block)
148
+ prefix = take_while(&block)
149
+ suffix = drop(prefix.length)
150
+ return prefix, suffix
151
+ end
152
+
153
+ # Splits the array into prefix/suffix pair according to the predicate.
154
+ #
155
+ # @return [(Array, Array)]
156
+ def split_when(&block)
157
+ prefix = take_until(&block)
158
+ suffix = drop(prefix.length)
159
+ return prefix, suffix
160
+ end
161
+
162
+ # @endgroup
163
+ #############################################################################
164
+ end
165
+
166
+ require "fr/functor/array"
167
+ require "fr/monoid/array"
168
+ require "fr/monad/array"
data/lib/fr/boolean.rb ADDED
@@ -0,0 +1,15 @@
1
+ class TrueClass
2
+ # true.maybe(value) => Fr::Some(value)
3
+ def maybe(value)
4
+ Fr::Maybe::Some.new(value)
5
+ end
6
+ end
7
+
8
+ class FalseClass
9
+ # false.maybe(value) => Fr::None
10
+ def maybe(value)
11
+ Fr::Maybe::None
12
+ end
13
+ end
14
+
15
+ require "fr/monoid/boolean"
data/lib/fr/either.rb ADDED
@@ -0,0 +1,94 @@
1
+ module Fr
2
+
3
+ class Either
4
+
5
+ class << self
6
+ # :: [Either a b] -> [a]
7
+ def lefts(es)
8
+ es.inject([]) do |ls,e|
9
+ e.fold(lambda{|l| ls.push(l) }, lambda{|_| ls })
10
+ end
11
+ end
12
+
13
+ # :: [Either a b] -> [b]
14
+ def rights(es)
15
+ es.inject([]) do |rs,e|
16
+ e.fold(lambda{|_| rs }, lambda{|r| rs.push(r) })
17
+ end
18
+ end
19
+
20
+ # :: [Either a b] -> [a] -> [b]
21
+ def partition(es)
22
+ es.inject([[],[]]) do |(ls,rs),e|
23
+ e.fold(lambda{|l| [ls.push(l), rs] },
24
+ lambda{|r| [ls, rs.push(r)] })
25
+ end
26
+ end
27
+ end
28
+
29
+ class Left < Either
30
+ def initialize(value)
31
+ @value = value
32
+ end
33
+
34
+ # :: Either a b -> (a -> c) -> (b -> c) -> c
35
+ def fold(left, right)
36
+ left.call(@value)
37
+ end
38
+
39
+ def map(&f)
40
+ self
41
+ end
42
+
43
+ def right?
44
+ false
45
+ end
46
+
47
+ def left?
48
+ true
49
+ end
50
+
51
+ def eql?(other)
52
+ Either === other and
53
+ other.fold(lambda{|o| o == @value }, lambda{|_| false })
54
+ end
55
+
56
+ alias_method :==, :eql?
57
+ end
58
+
59
+ class Right < Either
60
+ def initialize(value)
61
+ @value = value
62
+ end
63
+
64
+ # :: Either a b -> (a -> c) -> (b -> c) -> c
65
+ def fold(left, right)
66
+ right.call(@value)
67
+ end
68
+
69
+ def map(&f)
70
+ Right.new(f.call(@value))
71
+ end
72
+
73
+ def right?
74
+ true
75
+ end
76
+
77
+ def left?
78
+ false
79
+ end
80
+
81
+ def eql?(other)
82
+ Either === other and
83
+ other.fold(lambda{|_| false }, lambda{|o| o == @value })
84
+ end
85
+
86
+ alias_method :==, :eql?
87
+ end
88
+ end
89
+
90
+ end
91
+
92
+ require "fr/functor/either"
93
+ require "fr/monoid/either"
94
+ require "fr/monad/either"
data/lib/fr/errors.rb ADDED
@@ -0,0 +1,5 @@
1
+ module Fr
2
+ module Errors
3
+ autoload :ZipperError, "fr/errors/zipper_error"
4
+ end
5
+ end
@@ -0,0 +1,8 @@
1
+ module Fr
2
+ module Errors
3
+
4
+ class ZipperError < StandardError
5
+ end
6
+
7
+ end
8
+ end
data/lib/fr/functor.rb ADDED
@@ -0,0 +1,11 @@
1
+ module Fr
2
+ module Functor
3
+ # map :: Functor f => (a -> b) -> f a -> f b
4
+
5
+ # :: Functor f => f a -> f ()
6
+ def void(f)
7
+ map(lambda{|a| nil }, f)
8
+ end
9
+
10
+ end
11
+ end
@@ -0,0 +1,15 @@
1
+ #
2
+ # Because Array is a monad, we automatically derive
3
+ # the implementation for `map` from `liftM`.
4
+ #
5
+ # class << Array
6
+ # include Fr::Functor
7
+ #
8
+ # def map(f = nil, x = nil, &block)
9
+ # (x.nil?) ?
10
+ # (block_given?) ?
11
+ # f.map(&block) : # Array.map([..]){ .. }
12
+ # lambda{|x| x.map(&f) } :# Array.map(lambda{ .. })
13
+ # x.map(&f) # Array.map(lambda{ .. }, [..])
14
+ # end
15
+ # end