kleisli 0.0.1 → 0.0.2
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 +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +10 -0
- data/LICENSE.txt +1 -1
- data/README.md +58 -5
- data/lib/kleisli.rb +1 -0
- data/lib/kleisli/composition.rb +44 -0
- data/lib/kleisli/monad.rb +4 -0
- data/lib/kleisli/version.rb +1 -1
- data/test/kleisli/maybe_test.rb +2 -2
- metadata +12 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d1dcb8005f78840e1bebfc588e75a579e1f5057d
|
4
|
+
data.tar.gz: f1c1307b95152674449ea4822fc6b70c4344cd59
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5761487fddc055ad871ee6bcbbf4459eb06fe6eba201252dc5ddee9121057b804accb6286e99c360924cecd9e176daf20a3c2da4cf350a53d1afabb6aee40f80
|
7
|
+
data.tar.gz: 8e3c8e5b13ca0bc23abc9c7155fe0ac078d2e00e2a319a47ab380ae2ee25b30afe5118a0111cf8b7f4bbf7db93eed87bdfbed6b6cf9f8b454b1aee4a6accc2fb
|
data/.gitignore
CHANGED
data/.travis.yml
ADDED
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -1,8 +1,10 @@
|
|
1
|
-
# Kleisli
|
1
|
+
# Kleisli [](http://travis-ci.org/txus/adts)
|
2
2
|
|
3
|
-
An idiomatic, clean implementation of a few common useful monads in Ruby,
|
3
|
+
An idiomatic, clean implementation of a few common useful monads in Ruby,
|
4
|
+
written by [Ryan Levick][rylev] and me.
|
4
5
|
|
5
|
-
It aims to be idiomatic Ruby to use in
|
6
|
+
It aims to be idiomatic Ruby to use in Enter-Prise production apps, not a proof
|
7
|
+
of concept.
|
6
8
|
|
7
9
|
In your Gemfile:
|
8
10
|
|
@@ -14,9 +16,54 @@ We would like to thank Curry and Howard for their correspondence.
|
|
14
16
|
|
15
17
|
## Notation
|
16
18
|
|
17
|
-
For all its monads, Kleisli implements `return` (we call it `lift` instead, as
|
19
|
+
For all its monads, Kleisli implements `return` (we call it `lift` instead, as
|
20
|
+
`return` is a reserved keyword in Ruby) with convenience global methods (see
|
21
|
+
which for each monad below).
|
18
22
|
|
19
|
-
Kleisli uses a clever Ruby syntax trick to implement the `bind` operator, which
|
23
|
+
Kleisli uses a clever Ruby syntax trick to implement the `bind` operator, which
|
24
|
+
looks like this: `>->` when used with a block. We will probably burn in hell
|
25
|
+
for this. You can also use `>` or `>>` if you're going to pass in a proc or
|
26
|
+
lambda object.
|
27
|
+
|
28
|
+
### Function composition
|
29
|
+
|
30
|
+
You can use Haskell-like function composition with F and the familiar `.`. This
|
31
|
+
is such a perversion of Ruby syntax that Matz would probably condemn this:
|
32
|
+
|
33
|
+
Think of `F` as the identity function. Although it's just a hack to make it
|
34
|
+
work in Ruby.
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
# Reminder that (f . g) x= f(g(x))
|
38
|
+
f = F . first . last
|
39
|
+
f.call [[1,2], [3,4]]
|
40
|
+
# => 3
|
41
|
+
|
42
|
+
f = F . capitalize . reverse
|
43
|
+
f.call "hello"
|
44
|
+
# => "Olleh"
|
45
|
+
```
|
46
|
+
|
47
|
+
Functions and methods are interchangeable:
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
def foo(s)
|
51
|
+
s.reverse
|
52
|
+
end
|
53
|
+
|
54
|
+
f = F . capitalize . foo
|
55
|
+
f.call "hello"
|
56
|
+
# => "Olleh"
|
57
|
+
```
|
58
|
+
|
59
|
+
All functions and methods are partially applicable:
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
|
63
|
+
f = F . split(":") . strip
|
64
|
+
puts f.call " localhost:9092 "
|
65
|
+
# => ["localhost", "9092"]
|
66
|
+
```
|
20
67
|
|
21
68
|
## Maybe monad
|
22
69
|
|
@@ -40,6 +87,9 @@ maybe_user = Maybe(user) >-> user {
|
|
40
87
|
# You can also use Some and None as type constructors yourself.
|
41
88
|
x = Some(10)
|
42
89
|
y = None()
|
90
|
+
|
91
|
+
# Now using fancy point-free style:
|
92
|
+
Maybe(user) >> F . Maybe . address >> F . Maybe . street
|
43
93
|
```
|
44
94
|
|
45
95
|
### `fmap`
|
@@ -233,6 +283,9 @@ writer = Writer([], 100).fmap { |value|
|
|
233
283
|
|
234
284
|
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.
|
235
285
|
|
286
|
+
There's a catch unfortunately -- values passed to the functions are wrapped in
|
287
|
+
lambdas, so you need to call `.call` on them. See the examples below.
|
288
|
+
|
236
289
|
### `>->` (bind)
|
237
290
|
|
238
291
|
```ruby
|
data/lib/kleisli.rb
CHANGED
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'blankslate'
|
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
|
+
module Kleisli
|
14
|
+
class ComposedFn < BlankSlate
|
15
|
+
def initialize(fns=[])
|
16
|
+
@fns = fns
|
17
|
+
end
|
18
|
+
|
19
|
+
def method_missing(m, *args, &block)
|
20
|
+
fn = -> a, x {
|
21
|
+
|
22
|
+
puts "A: #{a.inspect}"
|
23
|
+
puts "M: #{m.inspect}"
|
24
|
+
puts "X: #{x.inspect}"
|
25
|
+
if x.respond_to?(m)
|
26
|
+
x.send(m, *a)
|
27
|
+
else
|
28
|
+
send(m, *a)
|
29
|
+
end
|
30
|
+
}.curry[args]
|
31
|
+
ComposedFn.new(@fns + [fn])
|
32
|
+
end
|
33
|
+
|
34
|
+
def call(*args)
|
35
|
+
@fns.reduce(:*).call(*args)
|
36
|
+
end
|
37
|
+
|
38
|
+
def to_ary
|
39
|
+
@fns.to_ary
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
F = Kleisli::ComposedFn.new
|
data/lib/kleisli/monad.rb
CHANGED
data/lib/kleisli/version.rb
CHANGED
data/test/kleisli/maybe_test.rb
CHANGED
@@ -10,11 +10,11 @@ class MaybeTest < MiniTest::Unit::TestCase
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def test_bind_none
|
13
|
-
assert_equal None(), None()
|
13
|
+
assert_equal None(), None() >> F . Maybe . *(2)
|
14
14
|
end
|
15
15
|
|
16
16
|
def test_bind_some
|
17
|
-
assert_equal Some(6), Some(3)
|
17
|
+
assert_equal Some(6), Some(3) >> F . Maybe . *(2)
|
18
18
|
end
|
19
19
|
|
20
20
|
def test_fmap_none
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kleisli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Josep M. Bach
|
@@ -9,34 +9,34 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-10
|
12
|
+
date: 2014-11-10 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
requirements:
|
18
|
-
- -
|
18
|
+
- - ~>
|
19
19
|
- !ruby/object:Gem::Version
|
20
20
|
version: '1.7'
|
21
21
|
type: :development
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
|
-
- -
|
25
|
+
- - ~>
|
26
26
|
- !ruby/object:Gem::Version
|
27
27
|
version: '1.7'
|
28
28
|
- !ruby/object:Gem::Dependency
|
29
29
|
name: rake
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
31
31
|
requirements:
|
32
|
-
- -
|
32
|
+
- - ~>
|
33
33
|
- !ruby/object:Gem::Version
|
34
34
|
version: '10.0'
|
35
35
|
type: :development
|
36
36
|
prerelease: false
|
37
37
|
version_requirements: !ruby/object:Gem::Requirement
|
38
38
|
requirements:
|
39
|
-
- -
|
39
|
+
- - ~>
|
40
40
|
- !ruby/object:Gem::Version
|
41
41
|
version: '10.0'
|
42
42
|
description: Usable, idiomatic common monads in Ruby
|
@@ -47,13 +47,15 @@ executables: []
|
|
47
47
|
extensions: []
|
48
48
|
extra_rdoc_files: []
|
49
49
|
files:
|
50
|
-
-
|
50
|
+
- .gitignore
|
51
|
+
- .travis.yml
|
51
52
|
- Gemfile
|
52
53
|
- LICENSE.txt
|
53
54
|
- README.md
|
54
55
|
- Rakefile
|
55
56
|
- kleisli.gemspec
|
56
57
|
- lib/kleisli.rb
|
58
|
+
- lib/kleisli/composition.rb
|
57
59
|
- lib/kleisli/either.rb
|
58
60
|
- lib/kleisli/functor.rb
|
59
61
|
- lib/kleisli/future.rb
|
@@ -80,17 +82,17 @@ require_paths:
|
|
80
82
|
- lib
|
81
83
|
required_ruby_version: !ruby/object:Gem::Requirement
|
82
84
|
requirements:
|
83
|
-
- -
|
85
|
+
- - '>='
|
84
86
|
- !ruby/object:Gem::Version
|
85
87
|
version: '0'
|
86
88
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
87
89
|
requirements:
|
88
|
-
- -
|
90
|
+
- - '>='
|
89
91
|
- !ruby/object:Gem::Version
|
90
92
|
version: '0'
|
91
93
|
requirements: []
|
92
94
|
rubyforge_project:
|
93
|
-
rubygems_version: 2.
|
95
|
+
rubygems_version: 2.0.14
|
94
96
|
signing_key:
|
95
97
|
specification_version: 4
|
96
98
|
summary: Usable, idiomatic common monads in Ruby
|