kleisli 0.0.6 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
- ---
2
- SHA1:
3
- metadata.gz: 54cce46f431f0930414953f5d860ab9805f8aa38
4
- data.tar.gz: 03b56b5e9209404852e50d3a1ff639173efd82ff
5
- SHA512:
6
- metadata.gz: ca6dabae528183053604cf6adccf3a621e96d25fc5171d507113eeb5aaf2c7349cc21bceefe4e053275f62093381b3b737927cf76234ab63de3bbb3b0e03385d
7
- data.tar.gz: 1f19da23d4ca766b1c8b16323863fb750b197c0e84602d0b4b34f6316bc7bb3fc171d8ae868e5bb023b978801890be7d95a5b2efc6cf399bf78dd28d5a072698
1
+ ---
2
+ SHA1:
3
+ metadata.gz: faefce7df113e758631337927c7a3ec32f0305ff
4
+ data.tar.gz: 6e583da161293e7c603163f24dfd6af293c5445b
5
+ SHA512:
6
+ metadata.gz: 24c366c7151184fb84fc42938f4afd1473e75575ff82c7e0ee5255b44bcef9f1ec028c10bd0ac19d84635b6b9ef53c7b6332f8e5e048df2ed9d73833482ca12c
7
+ data.tar.gz: b8d1ad6965cfe41ddf33e72af50ac7d4cfbc62b07aa23873625af8981c78a35f5a271aeea14929a76a1894fb26860faba1867af02f5d1c340368815cdf2d28bc
data/README.md CHANGED
@@ -47,11 +47,9 @@ f.call "hello"
47
47
  Functions and methods are interchangeable:
48
48
 
49
49
  ```ruby
50
- def foo(s)
51
- s.reverse
52
- end
50
+ foo = lambda { |s| s.reverse }
53
51
 
54
- f = F . capitalize . foo
52
+ f = F . capitalize . fn(&foo)
55
53
  f.call "hello"
56
54
  # => "Olleh"
57
55
  ```
@@ -60,11 +58,24 @@ All functions and methods are partially applicable:
60
58
 
61
59
  ```ruby
62
60
 
61
+ # Partially applied method:
63
62
  f = F . split(":") . strip
64
- puts f.call " localhost:9092 "
63
+ f.call " localhost:9092 "
64
+ # => ["localhost", "9092"]
65
+
66
+ # Partially applied lambda:
67
+ my_split = lambda { |str, *args| str.split(*args) }
68
+ f = F . fn(":", &split)
69
+ f.call " localhost:9092 "
65
70
  # => ["localhost", "9092"]
66
71
  ```
67
72
 
73
+ Finally, for convenience, `F` is the identity function:
74
+
75
+ ```ruby
76
+ F.call(1) # => 1
77
+ ```
78
+
68
79
  ## Maybe monad
69
80
 
70
81
  The Maybe monad is useful to express a pipeline of computations that might
@@ -88,14 +99,12 @@ maybe_user = Maybe(user) >-> user {
88
99
  x = Some(10)
89
100
  y = None()
90
101
 
91
- # Now using fancy point-free style:
92
- Maybe(user) >> F . Maybe . address >> F . Maybe . street
93
102
  ```
94
103
 
95
- As always, using point-free style is much cleaner:
104
+ As usual (with Maybe and Either), using point-free style is much cleaner:
96
105
 
97
106
  ```ruby
98
- Maybe(user) >> F . Maybe . address >> F . Maybe . street
107
+ Maybe(user) >> F . fn(&Maybe) . address >> F . fn(&Maybe) . street
99
108
  ```
100
109
 
101
110
  ### `fmap`
@@ -206,6 +215,11 @@ result.value # => "value was less or equal than 1"
206
215
  # If it failed in the second block
207
216
  result # => Left("value was not even")
208
217
  result.value # => "value was not even"
218
+
219
+ # Point-free style bind!
220
+ result = Right(3) >> F . fn(&Right) . *(2)
221
+ result # => Right(6)
222
+ result.value # => 6
209
223
  ```
210
224
 
211
225
  ### `fmap`
@@ -225,6 +239,18 @@ result # => Right(20)
225
239
  result # => Left("wrong")
226
240
  ```
227
241
 
242
+ ### `or`
243
+
244
+ `or` does pretty much what would you expect:
245
+
246
+ ```ruby
247
+ require 'kleisli'
248
+
249
+ Right(10).or(Right(999)) # => Right(10)
250
+ Left("error").or(Left("new error")) # => Left("new error")
251
+ Left("error").or { |err| Left("new #{err}") } # => Left("new error")
252
+ ```
253
+
228
254
  ### `to_maybe`
229
255
 
230
256
  Sometimes it's useful to turn an `Either` into a `Maybe`. You can use
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
+ # Note: BlankSlate was renamed to Blank to avoid collisions in this project.
3
+ #
2
4
  #--
3
5
  # Copyright 2004, 2006 by Jim Weirich (jim@weirichhouse.org).
4
6
  # All rights reserved.
@@ -9,15 +11,15 @@
9
11
  #++
10
12
 
11
13
  ######################################################################
12
- # BlankSlate provides an abstract base class with no predefined
14
+ # Blank provides an abstract base class with no predefined
13
15
  # methods (except for <tt>\_\_send__</tt> and <tt>\_\_id__</tt>).
14
- # BlankSlate is useful as a base class when writing classes that
16
+ # Blank is useful as a base class when writing classes that
15
17
  # depend upon <tt>method_missing</tt> (e.g. dynamic proxies).
16
18
  #
17
- class BlankSlate
19
+ class Blank
18
20
  class << self
19
21
 
20
- # Hide the method named +name+ in the BlankSlate class. Don't
22
+ # Hide the method named +name+ in the Blank class. Don't
21
23
  # hide +instance_eval+ or any method beginning with "__".
22
24
  def hide(name)
23
25
  methods = instance_methods.map(&:to_sym)
@@ -48,21 +50,21 @@ end
48
50
 
49
51
  ######################################################################
50
52
  # Since Ruby is very dynamic, methods added to the ancestors of
51
- # BlankSlate <em>after BlankSlate is defined</em> will show up in the
52
- # list of available BlankSlate methods. We handle this by defining a
53
+ # Blank <em>after Blank is defined</em> will show up in the
54
+ # list of available Blank methods. We handle this by defining a
53
55
  # hook in the Object and Kernel classes that will hide any method
54
- # defined after BlankSlate has been loaded.
56
+ # defined after Blank has been loaded.
55
57
  #
56
58
  module Kernel
57
59
  class << self
58
60
  alias_method :blank_slate_method_added, :method_added
59
61
 
60
62
  # Detect method additions to Kernel and remove them in the
61
- # BlankSlate class.
63
+ # Blank class.
62
64
  def method_added(name)
63
65
  result = blank_slate_method_added(name)
64
66
  return result if self != Kernel
65
- BlankSlate.hide(name)
67
+ Blank.hide(name)
66
68
  result
67
69
  end
68
70
  end
@@ -76,11 +78,11 @@ class Object
76
78
  alias_method :blank_slate_method_added, :method_added
77
79
 
78
80
  # Detect method additions to Object and remove them in the
79
- # BlankSlate class.
81
+ # Blank class.
80
82
  def method_added(name)
81
83
  result = blank_slate_method_added(name)
82
84
  return result if self != Object
83
- BlankSlate.hide(name)
85
+ Blank.hide(name)
84
86
  result
85
87
  end
86
88
 
@@ -103,7 +105,7 @@ class Module
103
105
  result = blankslate_original_append_features(mod)
104
106
  return result if mod != Object
105
107
  instance_methods.each do |name|
106
- BlankSlate.hide(name)
108
+ Blank.hide(name)
107
109
  end
108
110
  result
109
111
  end
@@ -1,3 +1,5 @@
1
+ require_relative 'blank'
2
+
1
3
  class Proc
2
4
  def self.comp(f, g)
3
5
  lambda { |*args| f[g[*args]] }
@@ -9,26 +11,33 @@ class Proc
9
11
  end
10
12
 
11
13
  module Kleisli
12
- class ComposedFn < BasicObject
14
+ class ComposedFn < Blank
13
15
  include ::Kernel
14
16
 
15
17
  def initialize(fns=[])
16
18
  @fns = fns
17
19
  end
18
20
 
19
- def method_missing(m, *args, &block)
20
- fn = -> a, x {
21
- if x.respond_to?(m)
22
- x.send(m, *a)
23
- else
24
- send(m, *[x, a])
25
- end
21
+ def fn(*args, &block)
22
+ f = -> arguments, receiver {
23
+ block.call(receiver, *arguments)
24
+ }.curry[args]
25
+ ComposedFn.new(@fns + [f])
26
+ end
27
+
28
+ def method_missing(meth, *args, &block)
29
+ f = -> arguments, receiver {
30
+ receiver.send(meth, *arguments, &block)
26
31
  }.curry[args]
27
- ComposedFn.new(@fns + [fn])
32
+ ComposedFn.new(@fns + [f])
28
33
  end
29
34
 
30
35
  def call(*args)
31
- @fns.reduce(:*).call(*args)
36
+ if @fns.any?
37
+ @fns.reduce(:*).call(*args)
38
+ else
39
+ args.first
40
+ end
32
41
  end
33
42
 
34
43
  def to_ary
@@ -28,7 +28,7 @@ module Kleisli
28
28
  Maybe::Some.new(@right)
29
29
  end
30
30
 
31
- def or(other)
31
+ def or(other, &other_blk)
32
32
  self
33
33
  end
34
34
  end
@@ -52,13 +52,20 @@ module Kleisli
52
52
  Maybe::None.new
53
53
  end
54
54
 
55
- def or(other)
56
- other
55
+ def or(other, &other_blk)
56
+ if other_blk
57
+ other_blk.call(@left)
58
+ else
59
+ other
60
+ end
57
61
  end
58
62
  end
59
63
  end
60
64
  end
61
65
 
66
+ Right = Kleisli::Either::Right.method(:new)
67
+ Left = Kleisli::Either::Left.method(:new)
68
+
62
69
  def Right(v)
63
70
  Kleisli::Either::Right.new(v)
64
71
  end
data/lib/kleisli/maybe.rb CHANGED
@@ -50,8 +50,10 @@ module Kleisli
50
50
  end
51
51
  end
52
52
 
53
+ Maybe = Kleisli::Maybe.method(:lift)
54
+
53
55
  def Maybe(v)
54
- Kleisli::Maybe.lift(v)
56
+ Maybe.(v)
55
57
  end
56
58
 
57
59
  def None()
@@ -1,3 +1,3 @@
1
1
  module Kleisli
2
- VERSION = "0.0.6"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -0,0 +1,78 @@
1
+ require 'test_helper'
2
+
3
+ class CompositionTest < MiniTest::Unit::TestCase
4
+ def test_one_method
5
+ f = F . first
6
+ result = f.call([1])
7
+ assert Fixnum === result, "#{result} is not a number"
8
+ assert_equal 1, result
9
+ end
10
+
11
+ def test_two_methods
12
+ f = F . first . last
13
+ result = f.call([1, [2,3]])
14
+ assert Fixnum === result, "#{result} is not a number"
15
+ assert_equal 2, result
16
+ end
17
+
18
+ def test_one_function
19
+ my_first = lambda { |x| x.first }
20
+
21
+ f = F . fn(&my_first)
22
+ result = f.call([1])
23
+ assert Fixnum === result, "#{result} is not a number"
24
+ assert_equal 1, result
25
+ end
26
+
27
+ def test_two_functions
28
+ my_first = lambda { |x| x.first }
29
+ my_last = lambda { |x| x.last }
30
+
31
+ f = F . fn(&my_first) . fn(&my_last)
32
+ result = f.call([1, [2,3]])
33
+ assert Fixnum === result, "#{result} is not a number"
34
+ assert_equal 2, result
35
+ end
36
+
37
+ def test_one_function_one_block
38
+ my_last = lambda { |x| x.last }
39
+
40
+ f = F . fn { |x| x.first } . fn(&my_last)
41
+ result = f.call([1, [2,3]])
42
+ assert Fixnum === result, "#{result} is not a number"
43
+ assert_equal 2, result
44
+ end
45
+
46
+ def test_one_function_one_method
47
+ my_last = lambda { |x| x.last }
48
+
49
+ f = F . first . fn(&my_last)
50
+ result = f.call([1, [2,3]])
51
+ assert Fixnum === result, "#{result} is not a number"
52
+ assert_equal 2, result
53
+ end
54
+
55
+ def test_undefined_method
56
+ f = F . foo
57
+ assert_raises(NoMethodError) { f.call(1) }
58
+ end
59
+
60
+ def test_identity
61
+ assert_equal 1, F.call(1)
62
+ end
63
+
64
+ def test_partially_applied_method
65
+ f = F . split(":")
66
+ result = f.call("localhost:9092")
67
+ assert Array === result, "#{result} is not an array"
68
+ assert_equal ["localhost", "9092"], result
69
+ end
70
+
71
+ def test_partially_applied_fn
72
+ split = lambda { |x, *args| x.split(*args) }
73
+ f = F . fn(":", &split)
74
+ result = f.call("localhost:9092")
75
+ assert Array === result, "#{result} is not an array"
76
+ assert_equal ["localhost", "9092"], result
77
+ end
78
+ end
@@ -42,4 +42,8 @@ class EitherTest < MiniTest::Unit::TestCase
42
42
  def test_to_maybe_left
43
43
  assert_equal None(), Left("error").fmap { |x| x * 2 }.to_maybe
44
44
  end
45
+
46
+ def test_pointfree
47
+ assert_equal Right(10), Right(5) >> F . fn(&Right) . *(2)
48
+ end
45
49
  end
@@ -9,13 +9,13 @@ class MaybeTest < MiniTest::Unit::TestCase
9
9
  assert_equal nil, None().value
10
10
  end
11
11
 
12
- # def test_bind_none
13
- # assert_equal None(), None() >> F . Maybe . *(2)
14
- # end
12
+ def test_bind_none
13
+ assert_equal None(), None() >> F . fn(&Maybe) . *(2)
14
+ end
15
15
 
16
- # def test_bind_some
17
- # assert_equal Some(6), Some(3) >> F . Maybe . *(2)
18
- # end
16
+ def test_bind_some
17
+ assert_equal Some(6), Some(3) >> F . fn(&Maybe) . *(2)
18
+ end
19
19
 
20
20
  def test_fmap_none
21
21
  assert_equal None(), None().fmap { |x| x * 2 }
@@ -29,9 +29,4 @@ class TryTest < MiniTest::Unit::TestCase
29
29
  try = Try { 20 / 10 } >-> number { Try { 10 / number } }
30
30
  assert_equal 5, try.value
31
31
  end
32
-
33
- # def test_pointfree
34
- # try = Try { 20 / 10 } >> F . Try { 10 / 2 }
35
- # assert_equal 5, try.value
36
- # end
37
32
  end
metadata CHANGED
@@ -1,57 +1,61 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: kleisli
3
- version: !ruby/object:Gem::Version
4
- version: 0.0.6
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
5
  platform: ruby
6
- authors:
6
+ authors:
7
7
  - Josep M. Bach
8
8
  - Ryan Levick
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
-
13
- date: 2014-11-17 00:00:00 Z
14
- dependencies:
15
- - !ruby/object:Gem::Dependency
12
+ date: 2014-11-22 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
16
15
  name: bundler
17
- prerelease: false
18
- requirement: &id001 !ruby/object:Gem::Requirement
19
- requirements:
20
- - - ~>
21
- - !ruby/object:Gem::Version
22
- version: "1.6"
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '1.6'
23
21
  type: :development
24
- version_requirements: *id001
25
- - !ruby/object:Gem::Dependency
26
- name: rake
27
22
  prerelease: false
28
- requirement: &id002 !ruby/object:Gem::Requirement
29
- requirements:
30
- - - ~>
31
- - !ruby/object:Gem::Version
32
- version: "10.0"
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '1.6'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rake
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '10.0'
33
35
  type: :development
34
- version_requirements: *id002
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '10.0'
35
42
  description: Usable, idiomatic common monads in Ruby
36
- email:
43
+ email:
37
44
  - josep.m.bach@gmail.com
38
45
  - ryan.levick@gmail.com
39
46
  executables: []
40
-
41
47
  extensions: []
42
-
43
48
  extra_rdoc_files: []
44
-
45
- files:
46
- - .gitignore
47
- - .travis.yml
49
+ files:
50
+ - ".gitignore"
51
+ - ".travis.yml"
48
52
  - Gemfile
49
53
  - LICENSE.txt
50
54
  - README.md
51
55
  - Rakefile
52
56
  - kleisli.gemspec
53
57
  - lib/kleisli.rb
54
- - lib/kleisli/blankslate.rb
58
+ - lib/kleisli/blank.rb
55
59
  - lib/kleisli/composition.rb
56
60
  - lib/kleisli/either.rb
57
61
  - lib/kleisli/functor.rb
@@ -62,6 +66,7 @@ files:
62
66
  - lib/kleisli/try.rb
63
67
  - lib/kleisli/version.rb
64
68
  - lib/kleisli/writer.rb
69
+ - test/kleisli/composition_test.rb
65
70
  - test/kleisli/either_test.rb
66
71
  - test/kleisli/future_test.rb
67
72
  - test/kleisli/maybe_test.rb
@@ -70,32 +75,31 @@ files:
70
75
  - test/kleisli/writer_test.rb
71
76
  - test/test_helper.rb
72
77
  homepage: https://github.com/txus/kleisli
73
- licenses:
78
+ licenses:
74
79
  - MIT
75
80
  metadata: {}
76
-
77
81
  post_install_message:
78
82
  rdoc_options: []
79
-
80
- require_paths:
83
+ require_paths:
81
84
  - lib
82
- required_ruby_version: !ruby/object:Gem::Requirement
83
- requirements:
84
- - &id003
85
- - ">="
86
- - !ruby/object:Gem::Version
87
- version: "0"
88
- required_rubygems_version: !ruby/object:Gem::Requirement
89
- requirements:
90
- - *id003
85
+ required_ruby_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ required_rubygems_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
91
95
  requirements: []
92
-
93
96
  rubyforge_project:
94
- rubygems_version: 2.4.2
97
+ rubygems_version: 2.2.2
95
98
  signing_key:
96
99
  specification_version: 4
97
100
  summary: Usable, idiomatic common monads in Ruby
98
- test_files:
101
+ test_files:
102
+ - test/kleisli/composition_test.rb
99
103
  - test/kleisli/either_test.rb
100
104
  - test/kleisli/future_test.rb
101
105
  - test/kleisli/maybe_test.rb