rb-kgy-fp 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1 @@
1
+ # frozen_string_literal: true
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'singleton'
4
+
5
+ module Special
6
+ private
7
+ class UnimplementedError < RuntimeError
8
+ def to_s
9
+ "Unimplemented Error: Please check the code for implementation"
10
+ end
11
+ end
12
+
13
+ public
14
+ class PlaceHolder
15
+ include Singleton
16
+ end
17
+
18
+ PH = PlaceHolder.instance
19
+
20
+ class Optional
21
+ include Singleton
22
+ end
23
+
24
+ OPT = Optional.instance
25
+
26
+ UNIMPLEMENTED = UnimplementedError.new
27
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Array
4
+ def head
5
+ first
6
+ end
7
+
8
+ def tail
9
+ length <= 1 ? [] : last(length - 1)
10
+ end
11
+
12
+ def init
13
+ length == 0 ? [] : first(length - 1)
14
+ end
15
+
16
+ def xprod arr2
17
+ ret = []
18
+ self.each do |item1|
19
+ arr2.each do |item2|
20
+ ret << [item1, item2]
21
+ end
22
+ end
23
+ ret
24
+ end
25
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ class String
4
+ def head
5
+ self[0]
6
+ end
7
+
8
+ def tail
9
+ self[1...length] || ""
10
+ end
11
+
12
+ def init
13
+ length == 0 ? "" : self[0...(length - 1)]
14
+ end
15
+
16
+ def last
17
+ self[length - 1]
18
+ end
19
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'lib/special'
4
+ require 'lib/trait/applicative'
5
+
6
+ module Alternative
7
+ include Applicative
8
+
9
+ # empty is required as class method
10
+
11
+ def or other
12
+ raise Special::UNIMPLEMENTED
13
+ end
14
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lib/special"
4
+ require 'lib/fun'
5
+ require 'lib/curry_fun'
6
+ require 'lib/trait/functor'
7
+
8
+ module Applicative
9
+ include Functor
10
+
11
+ # implement require: apply | lift_a2
12
+ # of is required as class method
13
+
14
+ # <*>
15
+ def apply other
16
+ lift_a2(other, &Fun.method(:id))
17
+ end
18
+
19
+ def lift_a2 other, &fn
20
+ apply(other.map(&fn))
21
+ end
22
+
23
+ # *>
24
+ def replace other
25
+ update(Fun.method(:id)).apply(other)
26
+ end
27
+
28
+ # <*
29
+ def const other
30
+ lift_a2(other, &CurryFun.method(:const))
31
+ end
32
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'lib/special'
4
+ require 'lib/fun'
5
+
6
+ module BiFunctor
7
+ # implement require: bimap | (first, second)
8
+ def bimap map_fst, map_snd
9
+ second(&map_snd).first(&map_fst)
10
+ end
11
+
12
+ def first &fn
13
+ bimap fn, Fun.method(:id)
14
+ end
15
+
16
+ def second &fn
17
+ bimap Fun.method(:id), fn
18
+ end
19
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'lib/special'
4
+ require 'lib/curry_fun'
5
+
6
+ module Functor
7
+ def fmap &fn
8
+ raise Special::UNIMPLEMENTED
9
+ end
10
+
11
+ # <$
12
+ def update value
13
+ map(&CurryFun::const(value))
14
+ end
15
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'lib/special'
4
+ require 'lib/curry_fun'
5
+ require 'lib/trait/applicative'
6
+
7
+ module Monad
8
+ include Applicative
9
+
10
+ # >>=
11
+ def bind & fn
12
+ raise Special::UNIMPLEMENTED
13
+ end
14
+
15
+ # >>
16
+ def consume other
17
+ bind { |_| other }
18
+ end
19
+
20
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'lib/special'
4
+ require 'lib/trait/semi_group'
5
+
6
+ module Monoid
7
+ include SemiGroup
8
+
9
+ # implement require: mempty | mconcat
10
+
11
+ def self.mempty
12
+ mconcat []
13
+ end
14
+
15
+ def mappend other
16
+ assoc(other)
17
+ end
18
+
19
+ def self.mconcat ls
20
+ ls.reverse.reduce(mempty) do |acc, item|
21
+ acc.mappend(item)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SemiGroup
4
+ # <>
5
+ def assoc other
6
+ raise Special::UNIMPLEMENTED
7
+ end
8
+ end
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'lib/special'
4
+ require 'lib/fun'
5
+ require 'lib/curry_fun'
6
+ require 'lib/trait/semi_group'
7
+ require 'lib/trait/monad'
8
+ require 'lib/trait/bi_functor'
9
+
10
+ class Either
11
+ include SemiGroup, Monad, Comparable, BiFunctor
12
+
13
+ attr_reader :value
14
+
15
+ private
16
+
17
+ public
18
+
19
+ def initialize value
20
+ @value = value
21
+ end
22
+
23
+ def left?
24
+ raise Special::UNIMPLEMENTED
25
+ end
26
+
27
+ def right?
28
+ !left?
29
+ end
30
+
31
+ def self.of value
32
+ Right.new value
33
+ end
34
+
35
+ def either map_left, map_right
36
+ raise Special::UNIMPLEMENTED
37
+ end
38
+
39
+ def <=> other
40
+ if right?
41
+ other.left? ? -1 : @value <=> other.value
42
+ else
43
+ other.right? ? 1 : @value <=> other.value
44
+ end
45
+ end
46
+ end
47
+
48
+ class Left < Either
49
+ def assoc other
50
+ other
51
+ end
52
+
53
+ def fmap & fn
54
+ self
55
+ end
56
+
57
+ def apply(other)
58
+ self
59
+ end
60
+
61
+ def bind(&fn)
62
+ self
63
+ end
64
+
65
+ def left?
66
+ true
67
+ end
68
+
69
+ def to_s
70
+ "Left(#{@value})"
71
+ end
72
+
73
+ def either(map_left, _)
74
+ map_left.(@value)
75
+ end
76
+
77
+ def bimap(map_fst, map_snd)
78
+ Left.new map_fst.(@value)
79
+ end
80
+ end
81
+
82
+ class Right < Either
83
+ def assoc other
84
+ self
85
+ end
86
+
87
+ def fmap & fn
88
+ of fn.(@value)
89
+ end
90
+
91
+ def apply(other)
92
+ of @value.(other)
93
+ end
94
+
95
+ def bind(&fn)
96
+ fn.(@value)
97
+ end
98
+
99
+ def left?
100
+ false
101
+ end
102
+
103
+ def to_s
104
+ "Right(#{@value})"
105
+ end
106
+
107
+ def either(_, map_right)
108
+ map_right.(@value)
109
+ end
110
+
111
+ def bimap(map_fst, map_snd)
112
+ of map_snd.(@value)
113
+ end
114
+ end
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'lib/special'
4
+ require 'lib/fun'
5
+ require 'lib/curry_fun'
6
+ require 'lib/trait/monad'
7
+ require 'lib/trait/alternative'
8
+
9
+ class Maybe
10
+ include Monad, Alternative, Comparable
11
+
12
+ private
13
+
14
+ public
15
+
16
+ def just?
17
+ raise Special::UNIMPLEMENTED
18
+ end
19
+
20
+ def nothing?
21
+ !just?
22
+ end
23
+
24
+ def self.of value
25
+ Just.new value
26
+ end
27
+
28
+ def <=> other
29
+ if just?
30
+ other.nothing? ? -1 : @value <=> other.value
31
+ else
32
+ other.nothing? ? 0 : 1
33
+ end
34
+ end
35
+
36
+ def self.empty
37
+ Nothing.new
38
+ end
39
+
40
+ def consume(other)
41
+ replace other
42
+ end
43
+ end
44
+
45
+ class Just < Maybe
46
+ attr_reader :value
47
+ def initialize value
48
+ @value = value
49
+ end
50
+
51
+ def just?
52
+ true
53
+ end
54
+
55
+ def fmap(&fn)
56
+ of fn.(@value)
57
+ end
58
+
59
+ def apply(other)
60
+ other.fmap(&@value)
61
+ end
62
+
63
+ def lift_a2(other, &fn)
64
+ if other.just?
65
+ of fn.(@value, other.value)
66
+ else
67
+ Nothing.new
68
+ end
69
+ end
70
+
71
+ def replace(other)
72
+ other
73
+ end
74
+
75
+ def or(other)
76
+ self
77
+ end
78
+
79
+ def bind(&fn)
80
+ fn.(@value)
81
+ end
82
+
83
+ end
84
+
85
+ class Nothing < Maybe
86
+ def just?
87
+ false
88
+ end
89
+
90
+ def fmap(&fn)
91
+ self
92
+ end
93
+
94
+ def apply(other)
95
+ self
96
+ end
97
+
98
+ def lift_a2(other, &fn)
99
+ self
100
+ end
101
+
102
+ def replace(other)
103
+ self
104
+ end
105
+
106
+ def or other
107
+ other
108
+ end
109
+
110
+ def bind(&fn)
111
+ self
112
+ end
113
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'lib/trait/monad'
4
+ require 'lib/fun'
5
+ require 'lib/curry_fun'
6
+
7
+ class Reader
8
+ include Monad
9
+
10
+ private_class_method :new
11
+
12
+ def self.of &reader
13
+ new &reader
14
+ end
15
+
16
+ def self.ask
17
+ of &Fun.method(:id)
18
+ end
19
+
20
+ def self.asks &fn
21
+ of do |env|
22
+ fn.(ask.run_reader(env))
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def initialize &reader
29
+ @reader = reader
30
+ end
31
+
32
+ public
33
+
34
+ def fmap(&fn)
35
+ of do |env|
36
+ fn.(run_reader(env))
37
+ end
38
+ end
39
+
40
+ def apply(other)
41
+ of do |env|
42
+ run_reader(env).run_reader(env)
43
+ end
44
+ end
45
+
46
+ def bind(&fn)
47
+ of do |env|
48
+ fst = run_reader(env)
49
+ fn.(fst).run_reader(env)
50
+ end
51
+ end
52
+
53
+ def run_reader env
54
+ @reader.(env)
55
+ end
56
+
57
+ def map_reader &fn
58
+ of CurryFun.pipe(@reader, fn)
59
+ end
60
+
61
+ def with_reader &fn
62
+ of CurryFun.pipe(fn, @reader)
63
+ end
64
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'lib/trait/monad'
4
+
5
+ class State
6
+ include Monad
7
+
8
+ private_class_method :new
9
+
10
+ def self.of & action
11
+ new &action
12
+ end
13
+
14
+ def self.get
15
+ of { |s| [s, s] }
16
+ end
17
+
18
+ def self.put state
19
+ of { |_| [nil, state] }
20
+ end
21
+
22
+ private
23
+
24
+ def initialize & action
25
+ @action = action
26
+ end
27
+
28
+ public
29
+
30
+ def fmap(&fn)
31
+ of do |state|
32
+ run_state(state) => [value, this_state]
33
+ [fn.(value), this_state]
34
+ end
35
+ end
36
+
37
+ def apply(other)
38
+ of do |state|
39
+ run_state(state) => [fn, this_state]
40
+ other.run_state(this_state) => [value, other_state]
41
+ [fn.(value), other_state]
42
+ end
43
+ end
44
+
45
+ def bind & fn
46
+ of do |s|
47
+ run_state(s) => [value, this_state]
48
+ fn.(value).run_state(this_state)
49
+ end
50
+ end
51
+
52
+ def run_state state
53
+ @action.(state)
54
+ end
55
+
56
+ def eval_state state
57
+ run_state(state)[0]
58
+ end
59
+
60
+ def exec_state state
61
+ run_state(state)[1]
62
+ end
63
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'lib/trait/monad'
4
+
5
+ class Writer
6
+ include Monad
7
+
8
+ private_class_method :new
9
+
10
+ def self.of value, stack
11
+ new value, stack
12
+ end
13
+
14
+ def self.tell stack = []
15
+ of nil, stack
16
+ end
17
+
18
+ private
19
+
20
+ def initialize value = nil, stack = []
21
+ @value = value
22
+ @stack = stack
23
+ @stack.freeze
24
+ @stack.each { |item| item.freeze }
25
+ end
26
+
27
+ public
28
+
29
+ def fmap(&fn)
30
+ of fn.(@value), @stack
31
+ end
32
+
33
+ def apply(other)
34
+ of @value.(other.map_writer), [*@stack, *other.exec_writer]
35
+ end
36
+
37
+ def bind(&fn)
38
+ fn.(@value).run_writer => [new_value, new_stack]
39
+ of new_value, [*@stack, *new_stack]
40
+ end
41
+
42
+ def run_writer
43
+ [@value, @stack]
44
+ end
45
+
46
+ def exec_writer
47
+ @stack
48
+ end
49
+
50
+ def map_writer
51
+ @value
52
+ end
53
+ end
metadata ADDED
@@ -0,0 +1,61 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rb-kgy-fp
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Phil Lui
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-09-02 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Ruby FP lib for self usage. This lib included simple FP functions like
14
+ Rmada of JS, and different typeclass support as Haskell
15
+ email: phillui37@gmail.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - lib/rb-kgy-fp/curry_fun.rb
21
+ - lib/rb-kgy-fp/fun.rb
22
+ - lib/rb-kgy-fp/lib.rb
23
+ - lib/rb-kgy-fp/special.rb
24
+ - lib/rb-kgy-fp/std_ext/array.rb
25
+ - lib/rb-kgy-fp/std_ext/string.rb
26
+ - lib/rb-kgy-fp/trait/alternative.rb
27
+ - lib/rb-kgy-fp/trait/applicative.rb
28
+ - lib/rb-kgy-fp/trait/bi_functor.rb
29
+ - lib/rb-kgy-fp/trait/functor.rb
30
+ - lib/rb-kgy-fp/trait/monad.rb
31
+ - lib/rb-kgy-fp/trait/monoid.rb
32
+ - lib/rb-kgy-fp/trait/semi_group.rb
33
+ - lib/rb-kgy-fp/typeclass/either.rb
34
+ - lib/rb-kgy-fp/typeclass/maybe.rb
35
+ - lib/rb-kgy-fp/typeclass/reader.rb
36
+ - lib/rb-kgy-fp/typeclass/state.rb
37
+ - lib/rb-kgy-fp/typeclass/writer.rb
38
+ homepage: https://github.com/phillui-37/rb-kgy-fp
39
+ licenses:
40
+ - MIT
41
+ metadata: {}
42
+ post_install_message:
43
+ rdoc_options: []
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ requirements: []
57
+ rubygems_version: 3.4.10
58
+ signing_key:
59
+ specification_version: 4
60
+ summary: Ruby FP lib for self usage
61
+ test_files: []