kleisli 0.2.0 → 0.2.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.
- checksums.yaml +7 -7
- data/README.md +3 -43
- data/Rakefile +1 -1
- data/kleisli.gemspec +1 -0
- data/lib/kleisli.rb +0 -1
- data/lib/kleisli/blank.rb +1 -1
- data/lib/kleisli/composition.rb +4 -12
- data/lib/kleisli/version.rb +1 -1
- data/test/kleisli/composition_test.rb +1 -1
- data/test/kleisli/either_test.rb +1 -1
- data/test/kleisli/future_test.rb +1 -1
- data/test/kleisli/maybe_test.rb +1 -1
- data/test/kleisli/try_test.rb +1 -1
- data/test/test_helper.rb +2 -1
- metadata +55 -54
- data/lib/kleisli/applicative_functor.rb +0 -4
- data/lib/kleisli/monoid.rb +0 -65
- data/lib/kleisli/writer.rb +0 -36
- data/test/kleisli/monoid_test.rb +0 -23
- data/test/kleisli/writer_test.rb +0 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
---
|
2
|
-
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
5
|
-
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0edc56dbf5e93057c443c0957b312524b8d20d7d
|
4
|
+
data.tar.gz: dee2ed0829f1ad9c4ad19f15a8f86293a79e2363
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 890887f61bd1b551028fea3384da9654e5394fa4da1ee993d3bf77ac5f43befe3b7ec64cd28002178dc933952cf0ecc638c694c37a88d87a64bcfeddfed1d068
|
7
|
+
data.tar.gz: 513db7d077f6061c734c6cde0fa7ccee7ba94e64b8b5195cce1d49642311ec4360c96dffba20cd44ef86b914d5aae0ae1461a86bf42e85fdbb12f8042873a623
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Kleisli [](http://travis-ci.org/txus/kleisli)
|
2
2
|
|
3
3
|
An idiomatic, clean implementation of a few common useful monads in Ruby,
|
4
4
|
written by [Ryan Levick][rylev] and me.
|
@@ -68,7 +68,7 @@ f.call " localhost:9092 "
|
|
68
68
|
|
69
69
|
# Partially applied lambda:
|
70
70
|
my_split = lambda { |str, *args| str.split(*args) }
|
71
|
-
f = F . fn(":", &
|
71
|
+
f = F . fn(":", &my_split) . strip
|
72
72
|
f.call " localhost:9092 "
|
73
73
|
# => ["localhost", "9092"]
|
74
74
|
```
|
@@ -261,7 +261,7 @@ require "kleisli"
|
|
261
261
|
|
262
262
|
add = -> x, y { x + y }
|
263
263
|
Right(add) * Right(10) * Right(2)
|
264
|
-
# =>
|
264
|
+
# => Right(12)
|
265
265
|
Right(add) * Left("error") * Right(2)
|
266
266
|
# => Left("error")
|
267
267
|
```
|
@@ -298,46 +298,6 @@ result # => Some(10)
|
|
298
298
|
result # => None()
|
299
299
|
```
|
300
300
|
|
301
|
-
## Writer
|
302
|
-
|
303
|
-
The Writer monad is arguably the least useful monad in Ruby (as side effects
|
304
|
-
are uncontrolled), but let's take a look at it anyway.
|
305
|
-
|
306
|
-
It is used to model computations that *append* to some kind of state (which
|
307
|
-
needs to be a Monoid, expressed in the `Kleisli::Monoid` mixin) at each step.
|
308
|
-
|
309
|
-
(We've already implemented the Monoid interface for `String`, `Array`, `Hash`,
|
310
|
-
`Fixnum` and `Float` for you.)
|
311
|
-
|
312
|
-
An example would be a pipeline of computations that append to a log, for
|
313
|
-
example a list of strings.
|
314
|
-
|
315
|
-
### `>->` (bind)
|
316
|
-
|
317
|
-
```ruby
|
318
|
-
require "kleisli"
|
319
|
-
|
320
|
-
writer = Writer([], 100) >-> value {
|
321
|
-
Writer(["added 100"], value + 100)
|
322
|
-
} >-> value {
|
323
|
-
Writer(["added 140 more"], value + 140)
|
324
|
-
} # => Writer(["added 100", "added 140 more"], 340)
|
325
|
-
|
326
|
-
log, value = writer.unwrap
|
327
|
-
log # => ["added 100", "added 140 more"]
|
328
|
-
value # => 340
|
329
|
-
```
|
330
|
-
|
331
|
-
### `fmap`
|
332
|
-
|
333
|
-
```ruby
|
334
|
-
require "kleisli"
|
335
|
-
|
336
|
-
writer = Writer([], 100).fmap { |value|
|
337
|
-
value + 100
|
338
|
-
} # => Writer([], 200)
|
339
|
-
```
|
340
|
-
|
341
301
|
## Future
|
342
302
|
|
343
303
|
The Future monad models a pipeline of computations that will happen in the future, as soon as the value needed for each step is available. It is useful to model, for example, a sequential chain of HTTP calls.
|
data/Rakefile
CHANGED
data/kleisli.gemspec
CHANGED
data/lib/kleisli.rb
CHANGED
data/lib/kleisli/blank.rb
CHANGED
@@ -24,7 +24,7 @@ class Blank
|
|
24
24
|
def hide(name)
|
25
25
|
methods = instance_methods.map(&:to_sym)
|
26
26
|
if methods.include?(name.to_sym) and
|
27
|
-
name !~ /^(__|instance_eval)/
|
27
|
+
name !~ /^(__|instance_eval|object_id)/
|
28
28
|
@hidden_methods ||= {}
|
29
29
|
@hidden_methods[name.to_sym] = instance_method(name)
|
30
30
|
undef_method name
|
data/lib/kleisli/composition.rb
CHANGED
@@ -1,18 +1,10 @@
|
|
1
1
|
require_relative 'blank'
|
2
2
|
|
3
|
-
class Proc
|
4
|
-
def self.comp(f, g)
|
5
|
-
lambda { |*args| f[g[*args]] }
|
6
|
-
end
|
7
|
-
|
8
|
-
def *(g)
|
9
|
-
Proc.comp(self, g)
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
3
|
module Kleisli
|
14
4
|
class ComposedFn < Blank
|
15
|
-
|
5
|
+
def self.comp(f, g)
|
6
|
+
lambda { |*args| f[g[*args]] }
|
7
|
+
end
|
16
8
|
|
17
9
|
def initialize(fns=[])
|
18
10
|
@fns = fns
|
@@ -34,7 +26,7 @@ module Kleisli
|
|
34
26
|
|
35
27
|
def call(*args)
|
36
28
|
if @fns.any?
|
37
|
-
@fns.reduce(
|
29
|
+
@fns.reduce { |f, g| ComposedFn.comp(f, g) }.call(*args)
|
38
30
|
else
|
39
31
|
args.first
|
40
32
|
end
|
data/lib/kleisli/version.rb
CHANGED
data/test/kleisli/either_test.rb
CHANGED
data/test/kleisli/future_test.rb
CHANGED
data/test/kleisli/maybe_test.rb
CHANGED
data/test/kleisli/try_test.rb
CHANGED
data/test/test_helper.rb
CHANGED
metadata
CHANGED
@@ -1,61 +1,66 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: kleisli
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.1
|
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
|
-
|
14
|
-
|
12
|
+
|
13
|
+
date: 2014-12-18 00:00:00 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
15
16
|
name: bundler
|
16
|
-
requirement: !ruby/object:Gem::Requirement
|
17
|
-
requirements:
|
18
|
-
- - "~>"
|
19
|
-
- !ruby/object:Gem::Version
|
20
|
-
version: '1.6'
|
21
|
-
type: :development
|
22
17
|
prerelease: false
|
23
|
-
|
24
|
-
requirements:
|
25
|
-
- -
|
26
|
-
- !ruby/object:Gem::Version
|
27
|
-
version:
|
28
|
-
|
18
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
19
|
+
requirements:
|
20
|
+
- - ~>
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: "1.6"
|
23
|
+
type: :development
|
24
|
+
version_requirements: *id001
|
25
|
+
- !ruby/object:Gem::Dependency
|
29
26
|
name: rake
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
27
|
+
prerelease: false
|
28
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: "10.0"
|
35
33
|
type: :development
|
34
|
+
version_requirements: *id002
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: minitest
|
36
37
|
prerelease: false
|
37
|
-
|
38
|
-
requirements:
|
39
|
-
- -
|
40
|
-
- !ruby/object:Gem::Version
|
41
|
-
version:
|
38
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - ~>
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: "5.5"
|
43
|
+
type: :development
|
44
|
+
version_requirements: *id003
|
42
45
|
description: Usable, idiomatic common monads in Ruby
|
43
|
-
email:
|
46
|
+
email:
|
44
47
|
- josep.m.bach@gmail.com
|
45
48
|
- ryan.levick@gmail.com
|
46
49
|
executables: []
|
50
|
+
|
47
51
|
extensions: []
|
52
|
+
|
48
53
|
extra_rdoc_files: []
|
49
|
-
|
50
|
-
|
51
|
-
-
|
54
|
+
|
55
|
+
files:
|
56
|
+
- .gitignore
|
57
|
+
- .travis.yml
|
52
58
|
- Gemfile
|
53
59
|
- LICENSE.txt
|
54
60
|
- README.md
|
55
61
|
- Rakefile
|
56
62
|
- kleisli.gemspec
|
57
63
|
- lib/kleisli.rb
|
58
|
-
- lib/kleisli/applicative_functor.rb
|
59
64
|
- lib/kleisli/blank.rb
|
60
65
|
- lib/kleisli/composition.rb
|
61
66
|
- lib/kleisli/either.rb
|
@@ -63,48 +68,44 @@ files:
|
|
63
68
|
- lib/kleisli/future.rb
|
64
69
|
- lib/kleisli/maybe.rb
|
65
70
|
- lib/kleisli/monad.rb
|
66
|
-
- lib/kleisli/monoid.rb
|
67
71
|
- lib/kleisli/try.rb
|
68
72
|
- lib/kleisli/version.rb
|
69
|
-
- lib/kleisli/writer.rb
|
70
73
|
- test/kleisli/composition_test.rb
|
71
74
|
- test/kleisli/either_test.rb
|
72
75
|
- test/kleisli/future_test.rb
|
73
76
|
- test/kleisli/maybe_test.rb
|
74
|
-
- test/kleisli/monoid_test.rb
|
75
77
|
- test/kleisli/try_test.rb
|
76
|
-
- test/kleisli/writer_test.rb
|
77
78
|
- test/test_helper.rb
|
78
79
|
homepage: https://github.com/txus/kleisli
|
79
|
-
licenses:
|
80
|
+
licenses:
|
80
81
|
- MIT
|
81
82
|
metadata: {}
|
83
|
+
|
82
84
|
post_install_message:
|
83
85
|
rdoc_options: []
|
84
|
-
|
86
|
+
|
87
|
+
require_paths:
|
85
88
|
- lib
|
86
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
87
|
-
requirements:
|
88
|
-
-
|
89
|
-
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
version: '0'
|
89
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- &id004
|
92
|
+
- ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: "0"
|
95
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
96
|
+
requirements:
|
97
|
+
- *id004
|
96
98
|
requirements: []
|
99
|
+
|
97
100
|
rubyforge_project:
|
98
|
-
rubygems_version: 2.
|
101
|
+
rubygems_version: 2.4.4
|
99
102
|
signing_key:
|
100
103
|
specification_version: 4
|
101
104
|
summary: Usable, idiomatic common monads in Ruby
|
102
|
-
test_files:
|
105
|
+
test_files:
|
103
106
|
- test/kleisli/composition_test.rb
|
104
107
|
- test/kleisli/either_test.rb
|
105
108
|
- test/kleisli/future_test.rb
|
106
109
|
- test/kleisli/maybe_test.rb
|
107
|
-
- test/kleisli/monoid_test.rb
|
108
110
|
- test/kleisli/try_test.rb
|
109
|
-
- test/kleisli/writer_test.rb
|
110
111
|
- test/test_helper.rb
|
data/lib/kleisli/monoid.rb
DELETED
@@ -1,65 +0,0 @@
|
|
1
|
-
module Kleisli
|
2
|
-
module Monoid
|
3
|
-
def fold(others)
|
4
|
-
others.reduce(self) { |acc, x| acc.mappend x }
|
5
|
-
end
|
6
|
-
|
7
|
-
def mempty
|
8
|
-
raise NotImplementedError, "this monoid doesn't implement mpemty"
|
9
|
-
end
|
10
|
-
|
11
|
-
def mappend(other)
|
12
|
-
raise NotImplementedError, "this monoid doesn't implement mappend"
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
String.class_eval do
|
18
|
-
include Kleisli::Monoid
|
19
|
-
|
20
|
-
def mempty
|
21
|
-
""
|
22
|
-
end
|
23
|
-
|
24
|
-
alias mappend +
|
25
|
-
end
|
26
|
-
|
27
|
-
Array.class_eval do
|
28
|
-
include Kleisli::Monoid
|
29
|
-
|
30
|
-
def mempty
|
31
|
-
[]
|
32
|
-
end
|
33
|
-
|
34
|
-
alias mappend +
|
35
|
-
end
|
36
|
-
|
37
|
-
Hash.class_eval do
|
38
|
-
include Kleisli::Monoid
|
39
|
-
|
40
|
-
def mempty
|
41
|
-
{}
|
42
|
-
end
|
43
|
-
|
44
|
-
alias mappend merge
|
45
|
-
end
|
46
|
-
|
47
|
-
Fixnum.class_eval do
|
48
|
-
include Kleisli::Monoid
|
49
|
-
|
50
|
-
def mempty
|
51
|
-
0
|
52
|
-
end
|
53
|
-
|
54
|
-
alias mappend +
|
55
|
-
end
|
56
|
-
|
57
|
-
Float.class_eval do
|
58
|
-
include Kleisli::Monoid
|
59
|
-
|
60
|
-
def mempty
|
61
|
-
0
|
62
|
-
end
|
63
|
-
|
64
|
-
alias mappend +
|
65
|
-
end
|
data/lib/kleisli/writer.rb
DELETED
@@ -1,36 +0,0 @@
|
|
1
|
-
require 'kleisli/monad'
|
2
|
-
require 'kleisli/monoid'
|
3
|
-
|
4
|
-
module Kleisli
|
5
|
-
class Writer < Monad
|
6
|
-
def self.lift(log, value)
|
7
|
-
new(log, value)
|
8
|
-
end
|
9
|
-
|
10
|
-
def initialize(log, value)
|
11
|
-
@log, @value = log, value
|
12
|
-
end
|
13
|
-
|
14
|
-
def ==(other)
|
15
|
-
unwrap == other.unwrap
|
16
|
-
end
|
17
|
-
|
18
|
-
def >(f)
|
19
|
-
other_log, other_value = f.call(@value).unwrap
|
20
|
-
Writer.new(@log + other_log, other_value)
|
21
|
-
end
|
22
|
-
|
23
|
-
def fmap(&f)
|
24
|
-
new_value = f.call(@value)
|
25
|
-
Writer.new(@log, new_value)
|
26
|
-
end
|
27
|
-
|
28
|
-
def unwrap
|
29
|
-
[@log, @value]
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
def Writer(log, value)
|
35
|
-
Kleisli::Writer.lift(log, value)
|
36
|
-
end
|
data/test/kleisli/monoid_test.rb
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
|
3
|
-
class MonoidTest < MiniTest::Unit::TestCase
|
4
|
-
def test_string_fold
|
5
|
-
assert_equal "hellogoodbye", "hello".fold(%w(good bye))
|
6
|
-
end
|
7
|
-
|
8
|
-
def test_array_fold
|
9
|
-
assert_equal [1, 2, 3], [1].fold([[2], [3]])
|
10
|
-
end
|
11
|
-
|
12
|
-
def test_hash_fold
|
13
|
-
assert_equal({a: 1, b: 2, c: 3}, {a: 1}.fold([{b: 2}, {c: 3}]))
|
14
|
-
end
|
15
|
-
|
16
|
-
def test_fixnum_fold
|
17
|
-
assert_equal 6, 1.fold([2,3])
|
18
|
-
end
|
19
|
-
|
20
|
-
def test_float_fold
|
21
|
-
assert_equal 6.0, 1.0.fold([2.0,3.0])
|
22
|
-
end
|
23
|
-
end
|
data/test/kleisli/writer_test.rb
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
|
3
|
-
class WriterTest < MiniTest::Unit::TestCase
|
4
|
-
def test_unwrap
|
5
|
-
log, value = Writer("log", 100).unwrap
|
6
|
-
assert_equal "log", log
|
7
|
-
assert_equal 100, value
|
8
|
-
end
|
9
|
-
|
10
|
-
def test_bind
|
11
|
-
writer = Writer("foo", 100) >-> value { Writer("bar", value + 100) }
|
12
|
-
assert_equal Writer("foobar", 200), writer
|
13
|
-
end
|
14
|
-
|
15
|
-
def test_fmap
|
16
|
-
writer = Writer("foo", 100).fmap { |value| value + 100 }
|
17
|
-
assert_equal Writer("foo", 200), writer
|
18
|
-
end
|
19
|
-
end
|