rb-kgy-fp 1.0.0

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.
@@ -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: []