funkr 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.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in funkr.gemspec
4
+ gemspec
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
data/funkr.gemspec ADDED
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "funkr/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "funkr"
7
+ s.version = Funkr::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Paul Rivier"]
10
+ s.email = ["paul (dot) r (dot) ml (at) gmail (dot) com"]
11
+ s.homepage = "http://github.com/paul-r-ml/funkr"
12
+ s.summary = %q{[EXPERIMENTAL] Some functionnal constructs for ruby}
13
+ s.description = %q{[EXPERIMENTAL] Some functionnal constructs for ruby, like ADT, functors, monads}
14
+
15
+ s.rubyforge_project = "funkr"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+ end
data/lib/funkr.rb ADDED
@@ -0,0 +1,3 @@
1
+ module Funkr
2
+ # nada ...
3
+ end
@@ -0,0 +1,57 @@
1
+ require "funkr/adt/matcher"
2
+
3
+ module Funkr
4
+ class ADT
5
+
6
+ def initialize(const, *data)
7
+ @const, @data = const, data
8
+ end
9
+
10
+ def self.adt(*constructs)
11
+ build_adt(constructs)
12
+ build_matcher(constructs)
13
+ end
14
+
15
+ def self.match_method=(method)
16
+ @match_method = method
17
+ end
18
+
19
+ self.match_method = :safe
20
+
21
+ def self.matcher; @matcher; end
22
+
23
+ def match
24
+ m = self.class.matcher.new(normal_form)
25
+ yield m
26
+ m.run_match
27
+ end
28
+
29
+ def to_s
30
+ format("%s%s%s",
31
+ @const,
32
+ @data.any? ? " : " : "",
33
+ @data.map(&:inspect).join(" ") )
34
+ end
35
+
36
+ private
37
+
38
+ attr_reader :const, :data
39
+
40
+ def normal_form; [@const, *@data]; end
41
+
42
+ def self.build_adt(constructs)
43
+ constructs.each do |c,*d|
44
+ define_singleton_method(c) do |*data|
45
+ self.new(c,*data)
46
+ end
47
+ end
48
+ end
49
+
50
+ def self.build_matcher(constructs)
51
+ @matcher = Class.new(Funkr::Matcher) do
52
+ build_matchers(constructs)
53
+ end
54
+ end
55
+
56
+ end
57
+ end
@@ -0,0 +1,33 @@
1
+ module Funkr
2
+ class Matcher
3
+
4
+ def initialize(match)
5
+ @const, *@data = match
6
+ @runner = nil
7
+ @undefined = self.class.constructs.clone
8
+ end
9
+
10
+ def self.build_matchers(constructs)
11
+ @constructs = constructs
12
+ constructs.each do |c|
13
+ name, *data = c
14
+ define_method(name) do |&b|
15
+ @undefined.delete(name)
16
+ if @const == name then
17
+ @runner = b
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ def self.constructs; @constructs; end
24
+
25
+ def run_match
26
+ if @undefined.any? then
27
+ raise "Incomplete match, missing : #{@undefined.join(" ")}"
28
+ end
29
+ @runner.call(*@data)
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,5 @@
1
+ require 'funkr/categories/functor'
2
+ require 'funkr/categories/applicative'
3
+ require 'funkr/categories/alternative'
4
+ require 'funkr/categories/monoid'
5
+ require 'funkr/categories/monad'
@@ -0,0 +1,11 @@
1
+ module Funkr
2
+ module Categories
3
+ module Alternative
4
+
5
+ def or_else
6
+ raise "Alternative#or_else not implemented"
7
+ end
8
+
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,59 @@
1
+ module Funkr
2
+ module Categories
3
+ module Applicative
4
+
5
+ def apply
6
+ raise "Applicative#apply not implemented"
7
+ end
8
+
9
+ module ClassMethods
10
+ def curry_lift_proc(&block)
11
+ self.pure(block.curry)
12
+ end
13
+
14
+ def full_lift_proc(&block)
15
+ lambda do |*args|
16
+ args.inject(curry_lift_proc(&block)) do |a,e|
17
+ a.apply(e)
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+
24
+ def <=>(other)
25
+ proxy_comp(other){|a,b| a <=> b}
26
+ end
27
+
28
+ def ==(other)
29
+ proxy_comp(other){|a,b| a == b}
30
+ end
31
+
32
+ def <(other)
33
+ proxy_comp(other){|a,b| a < b}
34
+ end
35
+
36
+ def <=(other)
37
+ proxy_comp(other){|a,b| a <= b}
38
+ end
39
+
40
+ def >(other)
41
+ proxy_comp(other){|a,b| a > b}
42
+ end
43
+
44
+ def >=(other)
45
+ proxy_comp(other){|a,b| a >= b}
46
+ end
47
+
48
+
49
+ private
50
+
51
+ def proxy_comp(other,&block)
52
+ self.class.
53
+ curry_lift_proc(&block).
54
+ apply(self).apply(other)
55
+ end
56
+
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,18 @@
1
+ module Funkr
2
+ module Categories
3
+ module Functor
4
+
5
+ def map
6
+ raise "Functor#map not implemented"
7
+ end
8
+
9
+
10
+ module ClassMethods
11
+
12
+ end
13
+
14
+
15
+
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,23 @@
1
+ module Funkr
2
+ module Categories
3
+ module Monad
4
+
5
+ def bind(&block)
6
+ raise "Monad#bind not implemented"
7
+ end
8
+
9
+ module ClassMethods
10
+
11
+ def unit
12
+ raise "Monad.unit not implemented"
13
+ end
14
+
15
+ end
16
+
17
+ def bind_(&block)
18
+ self.bind{|*args| yield}
19
+ end
20
+
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,20 @@
1
+ module Funkr
2
+ module Categories
3
+ module Monoid
4
+
5
+ def mplus
6
+ raise "Monoid#mplus not implemented"
7
+ end
8
+
9
+ module ClassMethods
10
+ def mzero
11
+ raise "Monoid#mzero not implemented"
12
+ end
13
+
14
+ def mconcat(list)
15
+ list.inject(mzero){|a,e| a.mplus(e)}
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,13 @@
1
+ require "funkr/categories/monoid"
2
+
3
+ class Fixnum
4
+
5
+ include Monoid
6
+ extend Monoid::ClassMethods
7
+
8
+ def mplus(y)
9
+ self + y
10
+ end
11
+
12
+ end
13
+
@@ -0,0 +1,2 @@
1
+ require 'funkr/types/maybe'
2
+ require 'funkr/types/failable'
@@ -0,0 +1,81 @@
1
+ require 'funkr/adt/adt'
2
+ require 'funkr/categories'
3
+
4
+ module Funkr
5
+ module Types
6
+ class Failable < ADT
7
+
8
+ include Funkr::Categories
9
+
10
+ adt :ok, :failed
11
+
12
+ class << self
13
+ alias unit ok
14
+ alias pure ok
15
+ end
16
+
17
+ include Functor
18
+
19
+ def map(&block)
20
+ case self.const
21
+ when :ok then
22
+ Failable.ok(yield(*self.data))
23
+ else self
24
+ end
25
+ end
26
+
27
+ include Applicative
28
+
29
+ def apply(to)
30
+ self.match do |f_on|
31
+ f_on.ok do |f|
32
+ to.match do |t_on|
33
+ t_on.ok {|t| Failable.ok(f.call(t)) }
34
+ t_on.failed { to }
35
+ end
36
+ end
37
+ f_on.failed { self }
38
+ end
39
+ end
40
+
41
+
42
+ include Alternative
43
+
44
+ def or_else(&block)
45
+ case self.const
46
+ when :ok then self
47
+ else yield
48
+ end
49
+ end
50
+
51
+
52
+ include Monoid
53
+ extend Monoid::ClassMethods
54
+
55
+ def mplus(m_y)
56
+ self.match do |x_on|
57
+ x_on.failed { m_y }
58
+ x_on.ok do |x|
59
+ m_y.match do |y_on|
60
+ y_on.failed { self }
61
+ y_on.ok {|y| Failable.ok(x.mplus(y))}
62
+ end
63
+ end
64
+ end
65
+ end
66
+
67
+
68
+ include Monad
69
+ extend Monad::ClassMethods
70
+
71
+ def bind(&block)
72
+ case self.const
73
+ when :ok then yield(*self.data)
74
+ else self
75
+ end
76
+ end
77
+
78
+
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,95 @@
1
+ require 'funkr/adt/adt'
2
+ require 'funkr/categories'
3
+
4
+ module Funkr
5
+ module Types
6
+ class Maybe < ADT
7
+
8
+ include Funkr::Categories
9
+
10
+ adt :just, :nothing
11
+
12
+ ### Categories
13
+
14
+ include Functor
15
+
16
+ def map(&block)
17
+ self.match do |on|
18
+ on.just {|v| Maybe.just(yield(v))}
19
+ on.nothing { self }
20
+ end
21
+ end
22
+
23
+ include Applicative
24
+ extend Applicative::ClassMethods
25
+
26
+ def apply(to)
27
+ self.match do |f_on|
28
+ f_on.just do |f|
29
+ to.match do |t_on|
30
+ t_on.just {|t| Maybe.unit(f.call(t)) }
31
+ t_on.nothing { to }
32
+ end
33
+ end
34
+ f_on.nothing { self }
35
+ end
36
+ end
37
+
38
+ include Alternative
39
+
40
+ def or_else(&block)
41
+ self.match do |on|
42
+ on.just {|v| self}
43
+ on.nothing { yield }
44
+ end
45
+ end
46
+
47
+
48
+ include Monoid
49
+ extend Monoid::ClassMethods
50
+
51
+ def mplus(m_y)
52
+ self.match do |x_on|
53
+ x_on.nothing { m_y }
54
+ x_on.just do |x|
55
+ m_y.match do |y_on|
56
+ y_on.nothing { self }
57
+ y_on.just {|y| Maybe.just(x.mplus(y))}
58
+ end
59
+ end
60
+ end
61
+ end
62
+
63
+
64
+ include Monad
65
+ extend Monad::ClassMethods
66
+
67
+ def bind(&block)
68
+ self.match do |on|
69
+ on.just {|v| yield(v)}
70
+ on.nothing {self}
71
+ end
72
+ end
73
+
74
+
75
+ class << self
76
+ alias unit just
77
+ alias pure just
78
+ alias mzero nothing
79
+ end
80
+
81
+ def self.box(value)
82
+ if value.nil? then self.nothing
83
+ else self.just(value) end
84
+ end
85
+
86
+ def unbox
87
+ self.match do |on|
88
+ on.just {|v| v }
89
+ on.nothing { nil }
90
+ end
91
+ end
92
+
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,3 @@
1
+ module Funkr
2
+ VERSION = "0.0.2"
3
+ end
data/test/tests.rb ADDED
@@ -0,0 +1,49 @@
1
+ require 'rubygems'
2
+ require 'funkr/types'
3
+
4
+ include Funkr::Types
5
+
6
+ m = Maybe.just(5)
7
+
8
+ puts(m.match do |on|
9
+ on.nothing{ Maybe.nothing }
10
+ on.just{|v| Maybe.just(v + 1) }
11
+ end.to_s)
12
+
13
+ puts(m.map{|v| v+1 })
14
+
15
+
16
+ n = Maybe.nothing
17
+
18
+ puts(n.match do |on|
19
+ on.nothing{ Maybe.nothing }
20
+ on.just{|v| Maybe.just(v + 1) }
21
+ end.to_s)
22
+
23
+
24
+ puts "\n> Curry lift"
25
+ f = Maybe.curry_lift_proc{|x,y| x + y}
26
+ puts f.apply(m).apply(m)
27
+ puts f.apply(m).apply(n)
28
+ puts f.apply(m).apply(n.or_else{Maybe.just(10)})
29
+
30
+ puts "\n> Full lift"
31
+ f = Maybe.full_lift_proc{|x,y| x + y}
32
+ puts f.call(m,m)
33
+ puts f.call(m,n)
34
+
35
+ # puts Maybe.mconcat([Maybe.just(10),
36
+ # Maybe.just(20),
37
+ # Maybe.nothing,
38
+ # Maybe.just(30)])
39
+
40
+ puts(m <=> m)
41
+ puts(m <=> (m.map{|v| v+1}))
42
+ puts(m <= (m.map{|v| v+1}))
43
+ puts(m <=> n)
44
+
45
+ puts "\n> Boxing and unboxing"
46
+ puts m.unbox
47
+ puts n.unbox
48
+ puts (Maybe.box(12)).unbox
49
+ puts (Maybe.box(nil)).unbox
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: funkr
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 2
9
+ version: 0.0.2
10
+ platform: ruby
11
+ authors:
12
+ - Paul Rivier
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-03-03 00:00:00 +01:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description: "[EXPERIMENTAL] Some functionnal constructs for ruby, like ADT, functors, monads"
22
+ email:
23
+ - paul (dot) r (dot) ml (at) gmail (dot) com
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files: []
29
+
30
+ files:
31
+ - .gitignore
32
+ - Gemfile
33
+ - Rakefile
34
+ - funkr.gemspec
35
+ - lib/funkr.rb
36
+ - lib/funkr/adt/adt.rb
37
+ - lib/funkr/adt/matcher.rb
38
+ - lib/funkr/categories.rb
39
+ - lib/funkr/categories/alternative.rb
40
+ - lib/funkr/categories/applicative.rb
41
+ - lib/funkr/categories/functor.rb
42
+ - lib/funkr/categories/monad.rb
43
+ - lib/funkr/categories/monoid.rb
44
+ - lib/funkr/extensions/fixnum.rb
45
+ - lib/funkr/types.rb
46
+ - lib/funkr/types/failable.rb
47
+ - lib/funkr/types/maybe.rb
48
+ - lib/funkr/version.rb
49
+ - test/tests.rb
50
+ has_rdoc: true
51
+ homepage: http://github.com/paul-r-ml/funkr
52
+ licenses: []
53
+
54
+ post_install_message:
55
+ rdoc_options: []
56
+
57
+ require_paths:
58
+ - lib
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ none: false
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ segments:
65
+ - 0
66
+ version: "0"
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ none: false
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ segments:
73
+ - 0
74
+ version: "0"
75
+ requirements: []
76
+
77
+ rubyforge_project: funkr
78
+ rubygems_version: 1.3.7
79
+ signing_key:
80
+ specification_version: 3
81
+ summary: "[EXPERIMENTAL] Some functionnal constructs for ruby"
82
+ test_files: []
83
+