Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Concurrent::Maybe #1

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
language: ruby
rvm: 2.6.3
sudo: false
script: ruby examples/*.rb
111 changes: 111 additions & 0 deletions examples/maybe.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
require 'concurrent'
require 'minitest/autorun'

=begin
Implement the following Haskell snippet
ghci> (*) <$> Just 2 <*> Just 8
Just 16
ghci> (++) <$> Just "klingon" <*> Nothing
Nothing
=end

class TestJustProduct < Minitest::Test

def setup
@just_a = Concurrent::Maybe.just(2)
@just_b = Concurrent::Maybe.just(8)
@just_result = Concurrent::Maybe.from do
(-> (x, y) { x * y }).curry.(@just_a).(@just_b)
end
@just_str = Concurrent::Maybe.just("klingon")
@nothing = Concurrent::Maybe.nothing
@nothing_result = Concurrent::Maybe.from do
(-> (x, y) { x + y }).curry.(@just_str).(@nothing)
end
end

def test_just_a
assert_equal true, @just_a.just?
assert_equal 2, @just_a.value
assert_equal false, @just_a.nothing?
assert_equal Concurrent::Maybe::NONE, @just_a.reason
assert_equal @just_a.value, @just_a.or(1)
end

# Failure on Just x + Just y
def test_just_result
assert_equal false, @just_result.just?
assert_equal Concurrent::Maybe::NONE, @just_result.value
assert_equal true, @just_result.nothing?
assert_equal NoMethodError, @just_result.reason.class
assert_equal 1, @just_result.or(1)
end

def test_nothing
assert_equal false, @nothing.just?
assert_equal Concurrent::Maybe::NONE, @nothing.value
assert_equal true, @nothing.nothing?
assert_equal StandardError, @nothing.reason.class
assert_equal 1, @nothing.or(1)
end

# Failure on Just x + Nothing
def test_nothing_result
assert_equal false, @nothing_result.just?
assert_equal Concurrent::Maybe::NONE, @nothing_result.value
assert_equal true, @nothing_result.nothing?
assert_equal NoMethodError, @nothing_result.reason.class
assert_equal 1, @nothing_result.or(1)
end

def test_misc
assert_raises NoMethodError do
[@just_a, @just_b].inject(&:+)
end
assert_equal 10, [@just_a, @just_b].map(&:value).inject(&:+)
assert_raises NoMethodError do
[@just_a, Concurrent::Maybe::NONE].map(&:value).inject(&:+)
end
end
end

=begin
fmap :: (a -> b) -> f a -> f b

instance Functor Maybe where
fmap f (Just x) = Just (f x)
fmap f Nothing = Nothing

Functor laws:
- fmap id = id
- fmap (f . g) = fmap f . fmap g
=end

def fmap(fproc, maybe)
return maybe if maybe.nothing?
Concurrent::Maybe.from { fproc.(maybe.value) }
end

class TestMaybeFunctor < Minitest::Test

def setup
@just_int = Concurrent::Maybe.just(4)
@nothing = Concurrent::Maybe.nothing
end

def test_id
id = ->(x) { x }
assert_equal @nothing, fmap(id, @nothing)
assert_equal @just_int, fmap(id, @just_int)
end

def test_composition
f = ->(x) { x ** 3 }
g = ->(x) { x - 3 }
assert_equal @nothing, fmap(f << g, @nothing)
assert_equal fmap(f, fmap(g, @nothing)), fmap(f << g, @nothing)
assert_equal Concurrent::Maybe.just(1), fmap(f << g, @just_int)
assert_equal fmap(f, fmap(g, @just_int)), fmap(f << g, @just_int)

end
end