funkr 0.0.21 → 0.0.22
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +1 -0
- data/Rakefile +9 -0
- data/funkr.gemspec +1 -0
- data/lib/funkr/adt/adt.rb +14 -9
- data/lib/funkr/adt/matcher.rb +1 -0
- data/lib/funkr/categories.rb +3 -0
- data/lib/funkr/extensions/array.rb +30 -21
- data/lib/funkr/extensions/hash.rb +53 -0
- data/lib/funkr/extensions.rb +1 -0
- data/lib/funkr/types/maybe.rb +25 -18
- data/lib/funkr/types/simple_record.rb +8 -10
- data/lib/funkr/version.rb +1 -1
- data/lib/funkr.rb +2 -1
- data/test/test_container.rb +17 -0
- data/test/test_extensions.rb +63 -0
- data/test/test_hash.rb +13 -0
- data/test/test_maybe.rb +73 -0
- data/test/test_simple_records.rb +25 -0
- metadata +42 -43
- data/test/tests.rb +0 -136
data/.travis.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm: 1.9.2
|
data/Rakefile
CHANGED
data/funkr.gemspec
CHANGED
@@ -18,4 +18,5 @@ Gem::Specification.new do |s|
|
|
18
18
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
19
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
20
|
s.require_paths = ["lib"]
|
21
|
+
s.add_development_dependency 'rake', '~> 0.9.2'
|
21
22
|
end
|
data/lib/funkr/adt/adt.rb
CHANGED
@@ -1,25 +1,30 @@
|
|
1
1
|
require "funkr/adt/matcher"
|
2
2
|
|
3
3
|
module Funkr
|
4
|
+
|
5
|
+
# Very rought Algebraic Data Types. A class inheriting from ADT can
|
6
|
+
# declare constructors with #adt
|
4
7
|
class ADT
|
5
8
|
|
6
9
|
def initialize(const, *data)
|
7
10
|
@const, @data = const, data
|
8
11
|
end
|
9
|
-
|
12
|
+
|
13
|
+
# Declare ADT constructors, for example :
|
14
|
+
# class Maybe < ADT; adt :just, :nothing; end
|
10
15
|
def self.adt(*constructs)
|
11
16
|
build_adt(constructs)
|
12
17
|
build_matcher(constructs)
|
13
18
|
end
|
14
19
|
|
15
|
-
def self.match_method=(method)
|
16
|
-
@match_method = method
|
17
|
-
end
|
18
|
-
|
19
|
-
self.match_method = :safe
|
20
|
-
|
21
20
|
def self.matcher; @matcher; end
|
22
21
|
|
22
|
+
# Match your ADT against its constructors, for example :
|
23
|
+
# a = Maybe.just("hello")
|
24
|
+
# a.match do |on|
|
25
|
+
# on.just{|x| puts x}
|
26
|
+
# on.nothing{ }
|
27
|
+
# end
|
23
28
|
def match
|
24
29
|
m = self.class.matcher.new(normal_form)
|
25
30
|
yield m
|
@@ -27,10 +32,10 @@ module Funkr
|
|
27
32
|
end
|
28
33
|
|
29
34
|
def to_s
|
30
|
-
format("%s%s%s",
|
35
|
+
format("{%s%s%s}",
|
31
36
|
@const,
|
32
37
|
@data.any? ? " : " : "",
|
33
|
-
@data.map(&:inspect).join(" ") )
|
38
|
+
@data.map(&:inspect).join(", ") )
|
34
39
|
end
|
35
40
|
|
36
41
|
private
|
data/lib/funkr/adt/matcher.rb
CHANGED
data/lib/funkr/categories.rb
CHANGED
@@ -1,62 +1,71 @@
|
|
1
1
|
require 'funkr/categories'
|
2
2
|
|
3
|
+
# Extends array (as list) capabilities
|
3
4
|
class Array
|
4
|
-
|
5
|
+
|
5
6
|
include Funkr::Categories
|
6
|
-
|
7
|
+
|
7
8
|
class << self
|
8
9
|
def unit(e); self.new([e]); end
|
9
10
|
alias pure unit
|
10
11
|
def mzero; self.new(); end
|
11
12
|
end
|
12
|
-
|
13
|
+
|
13
14
|
### Categories
|
14
|
-
|
15
|
-
#
|
16
|
-
|
17
|
-
|
18
|
-
|
15
|
+
|
16
|
+
# Array is already a functor with its correct map implementation
|
17
|
+
|
18
|
+
|
19
|
+
# Array can be made an applicative functor, for example :
|
20
|
+
# f = Array.curry_lift_proc{|x,y| x + y}
|
21
|
+
# f.apply([0,4]).apply([5,7]) => [5, 7, 9, 11]
|
22
|
+
# f.apply([0,4]).apply([]) => []
|
19
23
|
include Applicative
|
20
24
|
extend Applicative::ClassMethods
|
21
|
-
|
25
|
+
|
22
26
|
def apply(to)
|
23
27
|
map do |f|
|
24
28
|
to.map{ |t| f.call(t)}
|
25
29
|
end.flatten(1)
|
26
30
|
end
|
27
|
-
|
31
|
+
|
32
|
+
# Array is Alternative whith empty? being zero
|
28
33
|
include Alternative
|
29
|
-
|
34
|
+
|
35
|
+
# [].or_else{[5]} => [5]
|
30
36
|
def or_else(&block)
|
31
37
|
if empty? then yield
|
32
38
|
else self end
|
33
39
|
end
|
34
|
-
|
35
|
-
|
40
|
+
|
41
|
+
|
42
|
+
# Array is a monoid with mplus = (+) and mzero = []
|
36
43
|
include Monoid
|
37
44
|
extend Monoid::ClassMethods
|
38
|
-
|
45
|
+
|
46
|
+
# [5].mplus([6]) => [5,6]
|
39
47
|
def mplus(m_y)
|
40
48
|
self + m_y
|
41
49
|
end
|
42
|
-
|
43
|
-
|
50
|
+
|
51
|
+
|
52
|
+
# Array is also a monad
|
44
53
|
include Monad
|
45
54
|
extend Monad::ClassMethods
|
46
|
-
|
55
|
+
|
47
56
|
def bind(&block)
|
48
57
|
self.map(&block).flatten(1)
|
49
58
|
end
|
50
|
-
|
59
|
+
|
51
60
|
def self.box(value)
|
52
61
|
if value.nil? then self.mzero
|
53
62
|
else self.unit(value) end
|
54
63
|
end
|
55
|
-
|
64
|
+
|
56
65
|
def unbox()
|
57
66
|
if self.empty? then nil
|
58
67
|
else self.first end
|
59
68
|
end
|
60
|
-
|
61
|
-
|
69
|
+
|
70
|
+
|
62
71
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'funkr/categories'
|
3
|
+
|
4
|
+
# Extends Hash capabilities
|
5
|
+
class Hash
|
6
|
+
|
7
|
+
# maps function over hash values
|
8
|
+
def map_v # &block
|
9
|
+
h = Hash.new(self.default)
|
10
|
+
self.each{|k,v| h[k] = yield(v)}
|
11
|
+
return h
|
12
|
+
end
|
13
|
+
|
14
|
+
# maps function over hash keys
|
15
|
+
def map_k # &block
|
16
|
+
h = Hash.new(self.default)
|
17
|
+
self.each{|k,v| h[yield(k)] = v}
|
18
|
+
return h
|
19
|
+
end
|
20
|
+
|
21
|
+
# maps function over hash keys and values. Block should take
|
22
|
+
# 2 parameters, and should return a 2 elements array.
|
23
|
+
def map_kv # &block
|
24
|
+
h = Hash.new(self.default)
|
25
|
+
self.each do |k,v|
|
26
|
+
nk, nv = yield(k,v)
|
27
|
+
h[nk] = nv
|
28
|
+
end
|
29
|
+
return h
|
30
|
+
end
|
31
|
+
|
32
|
+
include Funkr::Categories
|
33
|
+
|
34
|
+
class << self
|
35
|
+
def mzero; self.new(); end
|
36
|
+
end
|
37
|
+
|
38
|
+
### Categories
|
39
|
+
|
40
|
+
# Hash is a functor via map_v, but unfortunatly a dumb map method is
|
41
|
+
# already defined by the enumerable module, and is kept for
|
42
|
+
# compatibility
|
43
|
+
|
44
|
+
|
45
|
+
# Hash is a monoid if values type is a monoid
|
46
|
+
include Monoid
|
47
|
+
extend Monoid::ClassMethods
|
48
|
+
|
49
|
+
def mplus(m_y)
|
50
|
+
self.merge(m_y){|k, v1, v2| v1.mplus(v2)}
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
data/lib/funkr/extensions.rb
CHANGED
data/lib/funkr/types/maybe.rb
CHANGED
@@ -4,25 +4,32 @@ require 'funkr/categories'
|
|
4
4
|
module Funkr
|
5
5
|
module Types
|
6
6
|
class Maybe < ADT
|
7
|
-
|
7
|
+
|
8
8
|
include Funkr::Categories
|
9
|
-
|
9
|
+
|
10
10
|
adt :just, :nothing
|
11
|
-
|
11
|
+
|
12
12
|
### Categories
|
13
|
-
|
13
|
+
|
14
14
|
include Functor
|
15
|
-
|
15
|
+
|
16
16
|
def map(&block)
|
17
17
|
self.match do |on|
|
18
18
|
on.just {|v| self.class.just(yield(v))}
|
19
19
|
on.nothing { self }
|
20
20
|
end
|
21
21
|
end
|
22
|
-
|
22
|
+
|
23
|
+
# Maybe can be made an applicative functor, for example :
|
24
|
+
# f = Maybe.curry_lift_proc{|x,y| x + y}
|
25
|
+
# a = Maybe.just(3)
|
26
|
+
# b = Maybe.just(4)
|
27
|
+
# c = Maybe.nothing
|
28
|
+
# f.apply(a).apply(b) => Just 7
|
29
|
+
# f.apply(a).apply(c) => Nothing
|
23
30
|
include Applicative
|
24
31
|
extend Applicative::ClassMethods
|
25
|
-
|
32
|
+
|
26
33
|
def apply(to)
|
27
34
|
self.match do |f_on|
|
28
35
|
f_on.just do |f|
|
@@ -34,20 +41,20 @@ module Funkr
|
|
34
41
|
f_on.nothing { self }
|
35
42
|
end
|
36
43
|
end
|
37
|
-
|
44
|
+
|
38
45
|
include Alternative
|
39
|
-
|
46
|
+
|
40
47
|
def or_else(&block)
|
41
48
|
self.match do |on|
|
42
49
|
on.just {|v| self}
|
43
50
|
on.nothing { yield }
|
44
51
|
end
|
45
52
|
end
|
46
|
-
|
47
|
-
|
53
|
+
|
54
|
+
|
48
55
|
include Monoid
|
49
56
|
extend Monoid::ClassMethods
|
50
|
-
|
57
|
+
|
51
58
|
def mplus(m_y)
|
52
59
|
self.match do |x_on|
|
53
60
|
x_on.nothing { m_y }
|
@@ -59,18 +66,18 @@ module Funkr
|
|
59
66
|
end
|
60
67
|
end
|
61
68
|
end
|
62
|
-
|
63
|
-
|
69
|
+
|
70
|
+
|
64
71
|
include Monad
|
65
72
|
extend Monad::ClassMethods
|
66
|
-
|
73
|
+
|
67
74
|
def bind(&block)
|
68
75
|
self.match do |on|
|
69
76
|
on.just {|v| yield(v)}
|
70
77
|
on.nothing {self}
|
71
78
|
end
|
72
79
|
end
|
73
|
-
|
80
|
+
|
74
81
|
def unbox(default=nil)
|
75
82
|
self.match do |on|
|
76
83
|
on.just {|v| v }
|
@@ -83,7 +90,7 @@ module Funkr
|
|
83
90
|
alias pure just
|
84
91
|
alias mzero nothing
|
85
92
|
end
|
86
|
-
|
93
|
+
|
87
94
|
def self.box(value)
|
88
95
|
if value.nil? then self.nothing
|
89
96
|
else self.just(value) end
|
@@ -94,7 +101,7 @@ module Funkr
|
|
94
101
|
# a list of all the Just values.
|
95
102
|
def self.concat(maybes)
|
96
103
|
maybes.inject([]) do |a, e|
|
97
|
-
e.match do |on|
|
104
|
+
e.match do |on|
|
98
105
|
on.just{|v| a + [v]}
|
99
106
|
on.nothing{ a }
|
100
107
|
end
|
@@ -62,20 +62,18 @@ module Funkr
|
|
62
62
|
|
63
63
|
def to_s; @key_vals.to_s; end
|
64
64
|
|
65
|
-
|
66
|
-
|
67
|
-
private
|
65
|
+
private
|
68
66
|
|
69
|
-
|
70
|
-
|
71
|
-
|
67
|
+
def rebuild_array
|
68
|
+
self.replace(@key_vals.values)
|
69
|
+
end
|
72
70
|
|
73
|
-
|
74
|
-
|
71
|
+
def check_keys(keys)
|
72
|
+
unless keys.all?{|k| @allowed_keys.include?(k)} then
|
75
73
|
raise "#{self.class.to_s} forbidden key in update"
|
74
|
+
end
|
76
75
|
end
|
77
|
-
end
|
78
|
-
|
79
76
|
|
77
|
+
end
|
80
78
|
end
|
81
79
|
end
|
data/lib/funkr/version.rb
CHANGED
data/lib/funkr.rb
CHANGED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'funkr/types/container'
|
3
|
+
|
4
|
+
class TestContainer < Test::Unit::TestCase
|
5
|
+
C = Funkr::Types::Container
|
6
|
+
|
7
|
+
def test_comparable
|
8
|
+
# assert_equal(C.new('foo'), C.new('foo'))
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_container
|
12
|
+
c = C.new("Value")
|
13
|
+
assert_equal("Value", c.unbox)
|
14
|
+
assert_equal("eulaV", c.map(&:reverse).unbox)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'funkr/extensions'
|
3
|
+
|
4
|
+
class TestExtensions < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def test_array_full_lift
|
7
|
+
a = [1,2,3]
|
8
|
+
b = [10,20,30]
|
9
|
+
f = Array.full_lift_proc{|x,y| x + y}
|
10
|
+
assert_equal([11, 21, 31, 12, 22, 32, 13, 23, 33], f.call(a,b))
|
11
|
+
assert_equal([], f.call(a,[]))
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_array_monad
|
15
|
+
a = [1,2,3]
|
16
|
+
b = [10,20,30]
|
17
|
+
assert_equal([11, 21, 31, 12, 22, 32, 13, 23, 33],
|
18
|
+
a.bind do |x|
|
19
|
+
b.bind do |y|
|
20
|
+
Array.unit(x + y)
|
21
|
+
end
|
22
|
+
end)
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_span
|
26
|
+
assert_equal([[1, 2, 4], [5, 7, 5, 8, 2, 10]],
|
27
|
+
[1,2,4,5,7,5,8,2,10].span{|x| x < 5})
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_group_seq_by
|
31
|
+
assert_equal([[1], [2, 4], [5, 7, 5], [8, 2, 10]],
|
32
|
+
[1,2,4,5,7,5,8,2,10].group_seq_by{|x| x % 2})
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_groups_of
|
36
|
+
assert_equal([[1, 2, 4, 5], [7, 5, 8, 2], [10]],
|
37
|
+
[1,2,4,5,7,5,8,2,10].groups_of(4))
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_sliding_groups_of
|
41
|
+
assert_equal([[1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6],
|
42
|
+
[5, 6, 7], [6, 7, 8], [7, 8, 9], [8, 9, 10]],
|
43
|
+
(1..10).to_a.sliding_groups_of(3))
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_seq_index
|
47
|
+
assert_equal(30, (0..100).to_a.seq_index([30,31,32]))
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_diff_with
|
51
|
+
a = [ {:v => 1}, {:v => 2}, {:v => 3}, {:v => 2}, {:v => 3} ]
|
52
|
+
b = [ {:v => 2}, {:v => 3}, {:v => 4}, {:v => 3}, {:v => 4} ]
|
53
|
+
assert_equal([[{:v=>1}], [{:v=>2}, {:v=>3}], [{:v=>4}]],
|
54
|
+
a.diff_with(b){|x,y| x[:v] == y[:v]})
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_make_uniq_by
|
58
|
+
a = [ {:v => 1}, {:v => 2}, {:v => 3}, {:v => 2}, {:v => 3}, {:v => 1} ]
|
59
|
+
assert_equal([{:v=>1}, {:v=>2}, {:v=>3}],
|
60
|
+
a.make_uniq_by{|x,y| x[:v] == y[:v]})
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
data/test/test_hash.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'funkr/extensions/hash'
|
3
|
+
|
4
|
+
class TestHash < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def test_map_v
|
7
|
+
assert_equal( {a: 2, b: 3},
|
8
|
+
{a: 1, b: 2}.map_v{|v| v + 1} )
|
9
|
+
h = Hash.new(:patate)
|
10
|
+
assert_equal( :patate, h.map_v{|x| x + 1}[:foo] )
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
data/test/test_maybe.rb
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'funkr/types/maybe'
|
3
|
+
require 'funkr/extensions'
|
4
|
+
|
5
|
+
class TestMaybe < Test::Unit::TestCase
|
6
|
+
|
7
|
+
M = Funkr::Types::Maybe
|
8
|
+
|
9
|
+
def j(v)
|
10
|
+
M.just(v)
|
11
|
+
end
|
12
|
+
|
13
|
+
def n
|
14
|
+
M.nothing
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_map
|
18
|
+
assert_equal(j(6), j(5).map{|v| v+1 })
|
19
|
+
assert_equal(n, j(5).map{|v| v+1})
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_curry_lift
|
23
|
+
f = M.curry_lift_proc{|x,y| x + y}
|
24
|
+
assert_equal(j(8), f.apply(j(5)).apply(j(3)))
|
25
|
+
assert_equal(n, f.apply(j(5)).apply(n))
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_or_else
|
29
|
+
assert_equal(j(10), n.or_else{j(10)})
|
30
|
+
assert_equal(j(4), j(4).or_else{j(2)})
|
31
|
+
# assert_nothing_raised(j(2).or_else{raise 'should not be raised'})
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_full_lift
|
35
|
+
f = M.full_lift_proc{|x,y| x + y}
|
36
|
+
assert_equal(j(6), f.call(j(4),j(2)))
|
37
|
+
assert_equal(n, f.call(j(1),n))
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_lift_with
|
41
|
+
assert_equal(j(7), M.lift_with(j(4),j(3)){|x,y| x + y})
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_mconcat
|
45
|
+
assert_equal(j(60), M.mconcat([j(10), j(20), n, j(30)]))
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_concat
|
49
|
+
assert_equal([10,20,30], M.concat([j(10), j(20), n, j(30)]))
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_comparisons
|
53
|
+
assert_equal(j(0), j(5) <=> j(5))
|
54
|
+
assert_equal(j(-1), j(5) <=> j(7))
|
55
|
+
assert_equal(j(1), j(2) <=> j(1))
|
56
|
+
assert_equal(j(true), j(3) < j(7))
|
57
|
+
assert_equal(n, j(4) <=> n)
|
58
|
+
assert_equal(n, n <=> j(2))
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_unbox
|
62
|
+
assert_equal(5, j(5).unbox)
|
63
|
+
assert_equal(5, j(5).unbox(2))
|
64
|
+
assert_equal(nil, n.unbox)
|
65
|
+
assert_equal(2, n.unbox(2))
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_box
|
69
|
+
assert_equal(j(5), M.box(5))
|
70
|
+
assert_equal(n, M.box(nil))
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'funkr/types/simple_record'
|
3
|
+
|
4
|
+
class TestSimpleRecords < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def test_simple_records
|
7
|
+
r = Funkr::Types::SimpleRecord.new(name: "Paul", age: 27, city: "Rennes")
|
8
|
+
name, age, city = r
|
9
|
+
assert_equal("Paul", name)
|
10
|
+
assert_equal(27, age)
|
11
|
+
assert_equal("Rennes", city)
|
12
|
+
name, age, city = r.with(age: 28, city: "Trouville")
|
13
|
+
assert_equal("Paul", name)
|
14
|
+
assert_equal(28, age)
|
15
|
+
assert_equal("Trouville", city)
|
16
|
+
|
17
|
+
r.name = "Paul R"
|
18
|
+
assert_equal("Paul R", r.name)
|
19
|
+
|
20
|
+
r.update!(name: "Paul")
|
21
|
+
n, _x, _y = r
|
22
|
+
assert_equal("Paul", n)
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
metadata
CHANGED
@@ -1,34 +1,37 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: funkr
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
|
6
|
-
- 0
|
7
|
-
- 0
|
8
|
-
- 21
|
9
|
-
version: 0.0.21
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.22
|
5
|
+
prerelease:
|
10
6
|
platform: ruby
|
11
|
-
authors:
|
7
|
+
authors:
|
12
8
|
- Paul Rivier
|
13
9
|
autorequire:
|
14
10
|
bindir: bin
|
15
11
|
cert_chain: []
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
12
|
+
date: 2011-10-14 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rake
|
16
|
+
requirement: &15155260 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 0.9.2
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *15155260
|
25
|
+
description: ! '[EXPERIMENTAL] Some functionnal constructs for ruby, like ADT, functors,
|
26
|
+
monads'
|
27
|
+
email:
|
23
28
|
- paul (dot) r (dot) ml (at) gmail (dot) com
|
24
29
|
executables: []
|
25
|
-
|
26
30
|
extensions: []
|
27
|
-
|
28
31
|
extra_rdoc_files: []
|
29
|
-
|
30
|
-
files:
|
32
|
+
files:
|
31
33
|
- .gitignore
|
34
|
+
- .travis.yml
|
32
35
|
- Gemfile
|
33
36
|
- Rakefile
|
34
37
|
- funkr.gemspec
|
@@ -46,44 +49,40 @@ files:
|
|
46
49
|
- lib/funkr/extensions/array.rb
|
47
50
|
- lib/funkr/extensions/enumerable.rb
|
48
51
|
- lib/funkr/extensions/fixnum.rb
|
52
|
+
- lib/funkr/extensions/hash.rb
|
49
53
|
- lib/funkr/types.rb
|
50
54
|
- lib/funkr/types/container.rb
|
51
55
|
- lib/funkr/types/failable.rb
|
52
56
|
- lib/funkr/types/maybe.rb
|
53
57
|
- lib/funkr/types/simple_record.rb
|
54
58
|
- lib/funkr/version.rb
|
55
|
-
- test/
|
56
|
-
|
59
|
+
- test/test_container.rb
|
60
|
+
- test/test_extensions.rb
|
61
|
+
- test/test_hash.rb
|
62
|
+
- test/test_maybe.rb
|
63
|
+
- test/test_simple_records.rb
|
57
64
|
homepage: http://github.com/paul-r-ml/funkr
|
58
65
|
licenses: []
|
59
|
-
|
60
66
|
post_install_message:
|
61
67
|
rdoc_options: []
|
62
|
-
|
63
|
-
require_paths:
|
68
|
+
require_paths:
|
64
69
|
- lib
|
65
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
70
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
66
71
|
none: false
|
67
|
-
requirements:
|
68
|
-
- -
|
69
|
-
- !ruby/object:Gem::Version
|
70
|
-
|
71
|
-
|
72
|
-
version: "0"
|
73
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ! '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
74
77
|
none: false
|
75
|
-
requirements:
|
76
|
-
- -
|
77
|
-
- !ruby/object:Gem::Version
|
78
|
-
|
79
|
-
- 0
|
80
|
-
version: "0"
|
78
|
+
requirements:
|
79
|
+
- - ! '>='
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
81
82
|
requirements: []
|
82
|
-
|
83
83
|
rubyforge_project: funkr
|
84
|
-
rubygems_version: 1.
|
84
|
+
rubygems_version: 1.8.10
|
85
85
|
signing_key:
|
86
86
|
specification_version: 3
|
87
|
-
summary:
|
87
|
+
summary: ! '[EXPERIMENTAL] Some functionnal constructs for ruby'
|
88
88
|
test_files: []
|
89
|
-
|
data/test/tests.rb
DELETED
@@ -1,136 +0,0 @@
|
|
1
|
-
$:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
|
2
|
-
|
3
|
-
require 'rubygems'
|
4
|
-
require 'funkr'
|
5
|
-
require 'funkr/types'
|
6
|
-
require 'funkr/extensions'
|
7
|
-
|
8
|
-
include Funkr::Types
|
9
|
-
|
10
|
-
m = Maybe.just(5)
|
11
|
-
|
12
|
-
puts "\n> All different ?"
|
13
|
-
puts([1,2,3].all_different?)
|
14
|
-
puts([1,2,2,3].all_different?)
|
15
|
-
|
16
|
-
puts "\n> Maybe"
|
17
|
-
puts(m.match do |on|
|
18
|
-
on.nothing{ Maybe.nothing }
|
19
|
-
on.just{|v| Maybe.just(v + 1) }
|
20
|
-
end.to_s)
|
21
|
-
|
22
|
-
puts(m.map{|v| v+1 })
|
23
|
-
|
24
|
-
|
25
|
-
n = Maybe.nothing
|
26
|
-
|
27
|
-
puts(n.match do |on|
|
28
|
-
on.nothing{ Maybe.nothing }
|
29
|
-
on.just{|v| Maybe.just(v + 1) }
|
30
|
-
end.to_s)
|
31
|
-
|
32
|
-
|
33
|
-
puts "\n> Curry lift"
|
34
|
-
f = Maybe.curry_lift_proc{|x,y| x + y}
|
35
|
-
puts f.apply(m).apply(m)
|
36
|
-
puts f.apply(m).apply(n)
|
37
|
-
puts f.apply(m).apply(n.or_else{Maybe.just(10)})
|
38
|
-
|
39
|
-
puts "\n> Full lift"
|
40
|
-
f = Maybe.full_lift_proc{|x,y| x + y}
|
41
|
-
puts f.call(m,m)
|
42
|
-
puts f.call(m,n)
|
43
|
-
|
44
|
-
puts "\n> Lift with"
|
45
|
-
puts Maybe.lift_with(m,m){|x,y| x + y}
|
46
|
-
|
47
|
-
puts "\n> mconcat"
|
48
|
-
puts Maybe.mconcat([Maybe.just(10),
|
49
|
-
Maybe.just(20),
|
50
|
-
Maybe.nothing,
|
51
|
-
Maybe.just(30)])
|
52
|
-
|
53
|
-
puts "\n> concat"
|
54
|
-
puts Maybe.concat([ Maybe.just(10),
|
55
|
-
Maybe.just(20),
|
56
|
-
Maybe.nothing,
|
57
|
-
Maybe.just(30) ]).inspect
|
58
|
-
|
59
|
-
puts "\n> Comparisons"
|
60
|
-
puts(m <=> m)
|
61
|
-
puts(m <=> (m.map{|v| v+1}))
|
62
|
-
puts(m < (m.map{|v| v+1}))
|
63
|
-
puts(m <=> n)
|
64
|
-
|
65
|
-
puts "\n> Boxing and unboxing"
|
66
|
-
puts m.unbox.inspect
|
67
|
-
puts n.unbox.inspect
|
68
|
-
puts (Maybe.box(12)).unbox.inspect
|
69
|
-
puts (Maybe.box(nil)).unbox.inspect
|
70
|
-
|
71
|
-
puts "\n> Containers"
|
72
|
-
c = Container.new("Value")
|
73
|
-
puts c.unbox
|
74
|
-
puts c.map(&:reverse)
|
75
|
-
|
76
|
-
a = [1,2,3]
|
77
|
-
b = [10,20,30]
|
78
|
-
|
79
|
-
puts "\n> Array full lift"
|
80
|
-
f = Array.full_lift_proc{|x,y| x + y}
|
81
|
-
puts f.call(a,b).inspect
|
82
|
-
puts f.call(a,[]).inspect
|
83
|
-
|
84
|
-
puts "\n> Array monad"
|
85
|
-
puts(a.bind do |x|
|
86
|
-
b.bind do |y|
|
87
|
-
Array.unit(x + y)
|
88
|
-
end
|
89
|
-
end.inspect)
|
90
|
-
|
91
|
-
|
92
|
-
puts "\n> span"
|
93
|
-
puts([1,2,4,5,7,5,8,2,10].span{|x| x < 5}.inspect)
|
94
|
-
|
95
|
-
puts "\n> group_seq_by"
|
96
|
-
puts([1,2,4,5,7,5,8,2,10].group_seq_by{|x| x % 2}.inspect)
|
97
|
-
|
98
|
-
puts "\n> groups_of"
|
99
|
-
puts([1,2,4,5,7,5,8,2,10].groups_of(4).inspect)
|
100
|
-
|
101
|
-
puts "\n> sliding_groups_of"
|
102
|
-
puts((1..10).to_a.sliding_groups_of(3).inspect)
|
103
|
-
|
104
|
-
puts "\n> seq_index (30)"
|
105
|
-
puts((0..100).to_a.seq_index([30,31,32]))
|
106
|
-
|
107
|
-
puts "\n> diff_with"
|
108
|
-
a = [ {:v => 1}, {:v => 2}, {:v => 3}, {:v => 2}, {:v => 3} ]
|
109
|
-
b = [ {:v => 2}, {:v => 3}, {:v => 4}, {:v => 3}, {:v => 4} ]
|
110
|
-
puts(a.diff_with(b){|x,y| x[:v] == y[:v]}.inspect)
|
111
|
-
|
112
|
-
puts "\n> make_uniq_by"
|
113
|
-
a = [ {:v => 1}, {:v => 2}, {:v => 3}, {:v => 2}, {:v => 3}, {:v => 1} ]
|
114
|
-
puts(a.make_uniq_by{|x,y| x[:v] == y[:v]}.inspect)
|
115
|
-
|
116
|
-
|
117
|
-
puts "\n> SimpleRecords"
|
118
|
-
r = SimpleRecord.new(name: "Paul", age: 27, city: "Rennes")
|
119
|
-
puts r.to_s
|
120
|
-
name, age, city = r
|
121
|
-
puts format("%s is %s and lives in %s", name, age, city)
|
122
|
-
name, age, city = r.with(age: 28, city: "Trouville")
|
123
|
-
puts format("%s is now %s and lives in %s", name, age, city)
|
124
|
-
puts format("%s is back to %s and really lives in %s", r.name, r.age, r.city)
|
125
|
-
r.name = "Paul R"
|
126
|
-
puts r.to_s
|
127
|
-
r.update!(name: "Paul")
|
128
|
-
n, _x, _y = r
|
129
|
-
puts n
|
130
|
-
|
131
|
-
class Person < SimpleRecord; fields :name, :age, :city; end
|
132
|
-
r = Person.new(name: "Paul", age: 27, city: "Rennes")
|
133
|
-
puts r.to_s
|
134
|
-
x = Person.new(name: "Paul", age: 27, city: "Rennes", genre: "Male") rescue "Forbidden"
|
135
|
-
y = Person.new(name: "Paul", age: 27) rescue "Forbidden"
|
136
|
-
|