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