tweed 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
@@ -0,0 +1,49 @@
1
+ <!--*-markdown-*-->
2
+
3
+ # Tweed
4
+
5
+ Tweed is a small pure-Ruby library for implementing [monads][].
6
+
7
+ [monads]: http://en.wikipedia.org/wiki/Monad_%28functional_programming%29
8
+
9
+ ## Examples
10
+
11
+ require 'tweed'
12
+
13
+ ### The Maybe Monad
14
+
15
+ Implementing [the Maybe monad][]:
16
+
17
+ [the maybe monad]: http://en.wikipedia.org/wiki/Monad_%28functional_programming%29#Maybe_monad
18
+
19
+ class Maybe < Tweed::Monad
20
+ define_monad do
21
+ construct { |value, success| @value, @success = value, success }
22
+ unit { |value| self.new(value, true) }
23
+ bind { |&block| @success ? block.call(@value) : self }
24
+ zero { self.new(nil, false) }
25
+ end
26
+ end
27
+
28
+ Using the Maybe monad to implement safe division:
29
+
30
+ module SafeDivision
31
+ def /(other)
32
+ self.bind do |x|
33
+ other.bind do |y|
34
+ y.zero? ? self.class.zero : self.class[x / y]
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ And then using the `SafeDivision` module:
41
+
42
+ class X < Maybe
43
+ include SafeDivision
44
+ end
45
+
46
+ success = X[1] / X[2.0] # => X[0.5]
47
+ failure = X[1] / X[0] # => X.zero
48
+
49
+ This will *not* result in a zero division error.
@@ -0,0 +1,24 @@
1
+ This is free and unencumbered software released into the public domain.
2
+
3
+ Anyone is free to copy, modify, publish, use, compile, sell, or
4
+ distribute this software, either in source code form or as a compiled
5
+ binary, for any purpose, commercial or non-commercial, and by any
6
+ means.
7
+
8
+ In jurisdictions that recognize copyright laws, the author or authors
9
+ of this software dedicate any and all copyright interest in the
10
+ software to the public domain. We make this dedication for the benefit
11
+ of the public at large and to the detriment of our heirs and
12
+ successors. We intend this dedication to be an overt act of
13
+ relinquishment in perpetuity of all present and future rights to this
14
+ software under copyright law.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ For more information, please refer to <http://unlicense.org/>
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,10 @@
1
+ class Object
2
+ def singleton_class
3
+ class << self; self; end
4
+ end
5
+ end
6
+
7
+ module Tweed
8
+ autoload :Monad, 'tweed/monad'
9
+ autoload :Monads, 'tweed/monads'
10
+ end
@@ -0,0 +1,156 @@
1
+ module Tweed
2
+ ##
3
+ # Abstract superclass to represent monads.
4
+ #
5
+ # To create a monad, subclass and define the monad’s Kleisli triple using
6
+ # {Monad.define_monad define_monad}.
7
+ #
8
+ # @example Implementing the Maybe monad
9
+ # class Maybe < Tweed::Monad
10
+ # define_monad do
11
+ # # This becomes the `initialize` instance method:
12
+ # construct { |value, success| @value, @success = value, success }
13
+ #
14
+ # # This becomes the `return` class method:
15
+ # unit { |value| self.new(value, true) }
16
+ #
17
+ # # This becomes the `bind` instance method:
18
+ # bind { |&block| @success ? block.call(@value) : self }
19
+ #
20
+ # # This becomes the (cached) `zero` class method:
21
+ # zero { self.new(nil, false) }
22
+ # end
23
+ #
24
+ # def inspect
25
+ # @success ? "Just #{@value.inspect}" : "Nothing"
26
+ # end
27
+ # end
28
+ #
29
+ # @example Using the Maybe monad to implement safe division
30
+ # class SafeDivision < Maybe
31
+ # def /(other)
32
+ # self.bind do |x|
33
+ # other.bind do |y|
34
+ # y.zero? ? self.class.zero : self.class[x / y]
35
+ # end
36
+ # end
37
+ # end
38
+ # end
39
+ #
40
+ # x = SafeDivision[1] / SafeDivision[2.0] # => Just 0.5
41
+ # y = SafeDivision[1] / SafeDivision[0] # => Nothing
42
+ #
43
+ class Monad
44
+
45
+ # Lift a value into this monad.
46
+ # @abstract Subclass and override using {Monad.define_monad define_monad}.
47
+ # @return [Monad] the newly created monad.
48
+ def self.return(*args)
49
+ abstract
50
+ end
51
+
52
+ # Pull an underlying value out of this monad.
53
+ # @abstract Subclass and override using {Monad.define_monad define_monad}.
54
+ def bind(&block)
55
+ abstract
56
+ end
57
+
58
+ # Syntactic sugar for {Monad.return Monad.return(...)}.
59
+ # @return [Monad]
60
+ def self.[](*args)
61
+ self.return(*args)
62
+ end
63
+
64
+ ##
65
+ # Adaptation of `liftM2` from Haskell. ‘Lifts’ a binary function into the
66
+ # current monad.
67
+ #
68
+ # @example Add two numbers
69
+ # m_x = SomeMonad[1]
70
+ # m_y = SomeMonad[2]
71
+ # m_z = m_x.lift_m2(m_y) { |x, y| x + y } # => SomeMonad[1 + 2] => SomeMonad[3]
72
+ #
73
+ # @param other another value within the current monad.
74
+ # @yield [x, y] the underlying values of `self` and `other`.
75
+ # @return the result of the binary operation, lifted back into the current monad.
76
+ def lift_m2(other)
77
+ self.bind do |x|
78
+ other.bind do |y|
79
+ self.class[yield(x, y)]
80
+ end
81
+ end
82
+ end
83
+
84
+ protected
85
+
86
+ ##
87
+ # Class method to set up a Monad subclass using a Kleisli triple.
88
+ #
89
+ # Uses a custom DSL to define the triple and an optional monadic zero.
90
+ #
91
+ # @example Implementing the List monad
92
+ # class List < Tweed::Monad
93
+ # define_monad do
94
+ # # This becomes the `initialize` instance method, so instance
95
+ # # variables will be set on the Monad instance.
96
+ # construct { |sequence| @sequence = sequence }
97
+ #
98
+ # # This becomes the `return` class method, also accessible via `[]`
99
+ # # on both the class and instances.
100
+ # unit { |value| self.new([value]) }
101
+ #
102
+ # # This becomes the `bind` instance method, and will always receive
103
+ # # a block; it should explicitly use `block.call` instead of `yield`
104
+ # # due to the way blocks work in Ruby (Ruby will complain that no
105
+ # # block was passed if you `yield`).
106
+ # bind { |&block| self.new(@sequence.map(&block).inject(&:+)) }
107
+ #
108
+ # # The definition of zero for this monad. This will be called once
109
+ # # from the class and have its result cached.
110
+ # zero { self.new([].freeze!) }
111
+ # end
112
+ # end
113
+ #
114
+ # @see http://en.wikipedia.org/wiki/Monad_(functional_programming)#Concepts
115
+ # Wikipedia article on Monads
116
+ def self.define_monad(&def_block)
117
+ # Capture the Kleisli triple, and perhaps a monadic zero.
118
+ capture = Object.new
119
+
120
+ class << capture
121
+ def construct(&block) @construct = block end
122
+ def unit(&block) @unit = block end
123
+ def bind(&block) @bind = block end
124
+ def zero(&block) @zero = block end
125
+ end
126
+
127
+ capture.instance_eval(&def_block)
128
+
129
+ class << capture
130
+ def [](sym) instance_variable_get("@#{sym}".to_sym) end
131
+ end
132
+
133
+ # Define the necessary methods on the monad, given the components of the
134
+ # Kleisli triple (and optionally the monadic zero).
135
+ self.class_eval do
136
+ define_method(:initialize, &capture[:construct]) unless capture[:construct].nil?
137
+ define_method(:bind, &capture[:bind]) unless capture[:bind].nil?
138
+ self.singleton_class.instance_eval do
139
+ define_method(:return, &capture[:unit]) unless capture[:unit].nil?
140
+ unless capture[:zero].nil?
141
+ define_method(:get_zero, &capture[:zero])
142
+ private(:get_zero)
143
+ define_method(:zero) { @zero ||= get_zero }
144
+ end
145
+ end
146
+ end
147
+
148
+ self
149
+ end # def self.define_monad
150
+ end # class Monad
151
+ end # module Tweed
152
+
153
+ # @private
154
+ def abstract
155
+ raise NotImplementedError
156
+ end
@@ -0,0 +1,7 @@
1
+ module Tweed
2
+ module Monads
3
+ autoload :Identity, 'tweed/monads/identity'
4
+ autoload :List, 'tweed/monads/list'
5
+ autoload :Maybe, 'tweed/monads/maybe'
6
+ end
7
+ end
@@ -0,0 +1,23 @@
1
+ module Tweed
2
+ module Monads
3
+ class Identity < Tweed::Monad
4
+ define_monad do
5
+ construct { |value| @value = value }
6
+ unit { |value| self.new(value) }
7
+ bind { |&block| block.call(@value) }
8
+ end
9
+
10
+ def inspect
11
+ "#{self.class.inspect}[#{@value.inspect}]"
12
+ end
13
+
14
+ def ==(other)
15
+ if other.is_a? Identity
16
+ @value == other.instance_eval { @value }
17
+ else
18
+ @value == other
19
+ end
20
+ end
21
+ end # class Identity
22
+ end # module Monads
23
+ end # module Tweed
@@ -0,0 +1,32 @@
1
+ module Tweed
2
+ module Monads
3
+ class List < Tweed::Monad
4
+ define_monad do
5
+ construct { |sequence| @sequence = sequence }
6
+ unit { |value| self.new([value]) }
7
+ bind do |&block|
8
+ self.class.new(@sequence.map do |item|
9
+ block.call(item).to_a
10
+ end.inject(&:+))
11
+ end
12
+ zero { self.new([].freeze!) }
13
+ end
14
+
15
+ def +(other)
16
+ self.class.new(self.to_a + other.to_a)
17
+ end
18
+
19
+ def inspect
20
+ "#{self.class.inspect} :: #{@sequence.inspect}"
21
+ end
22
+
23
+ def to_a
24
+ @sequence.to_a
25
+ end
26
+
27
+ def ==(other)
28
+ self.to_a == other.to_a
29
+ end
30
+ end # class List
31
+ end # module Monads
32
+ end # module Tweed
@@ -0,0 +1,37 @@
1
+ module Tweed
2
+ module Monads
3
+ ## The Maybe monad
4
+ class Maybe < Tweed::Monad
5
+ define_monad do
6
+ construct { |value, success| @value, @success = value, success }
7
+ unit { |value| self.new(value, true) }
8
+ bind { |&block| @success ? block.call(@value) : self }
9
+ zero { self.new(nil, false) }
10
+ end
11
+
12
+ def inspect
13
+ repr = @success ? "[#{@value.inspect}]" : ".zero"
14
+ "#{self.class.inspect}#{repr}"
15
+ end
16
+
17
+ def ==(other)
18
+ if other.is_a? Maybe
19
+ [@value, @success] == other.instance_eval { [@value, @success] }
20
+ else
21
+ self.object_id == other.object_id || @value == other
22
+ end
23
+ end
24
+
25
+ # An example of safe division implemented over the Maybe monad.
26
+ module SafeDivision
27
+ def /(other)
28
+ self.bind do |x|
29
+ other.bind do |y|
30
+ y.zero? ? self.class.zero : self.class[x / y]
31
+ end
32
+ end
33
+ end # def /
34
+ end # module SafeDivision
35
+ end # class Maybe
36
+ end # module Monads
37
+ end # module Tweed
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tweed
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Zachary Voase
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-01-14 00:00:00 +00:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: yard
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.5.2
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: bluecloth
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 2.0.5
34
+ version:
35
+ description: Tweed is an implementation of monads in Ruby.
36
+ email: zacharyvoase@me.com
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files: []
42
+
43
+ files:
44
+ - README
45
+ - UNLICENSE
46
+ - VERSION
47
+ - lib/tweed/monad.rb
48
+ - lib/tweed/monads/identity.rb
49
+ - lib/tweed/monads/list.rb
50
+ - lib/tweed/monads/maybe.rb
51
+ - lib/tweed/monads.rb
52
+ - lib/tweed.rb
53
+ has_rdoc: true
54
+ homepage: http://bitbucket.org/zacharyvoase/tweed
55
+ licenses:
56
+ - Public Domain
57
+ post_install_message:
58
+ rdoc_options: []
59
+
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: "0"
67
+ version:
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: "0"
73
+ version:
74
+ requirements: []
75
+
76
+ rubyforge_project:
77
+ rubygems_version: 1.3.5
78
+ signing_key:
79
+ specification_version: 3
80
+ summary: An implementation of monads in Ruby.
81
+ test_files: []
82
+