Daitai (代替, Japanese for "alternative") is a functional library for Ruby language.
Why is it different?
- Encourages Haskell's style of writing functions - the object you work on is the last parameter, so you can compose a sequence of operations on this object.
- Provides curried functions.
- Favors immutability.
- Eliminates side effects.
Installation
Add this line to your application's Gemfile:
gem 'daitai'
And then execute:
$ bundle
Or install it yourself as:
$ gem install daitai
Documentation
- abs
- add
- all
- always
- and
- any
- comparator
- compose
- concat
- cond
- dec
- divide
- equals
- false
- filter
- flip
- gt
- gte
- head
- identity
- inc
- init
- is
- is_nil
- last
- length
- lt
- lte
- map
- max
- mean
- median
- min
- modulo
- multiply
- negate
- not
- once
- or
- partition
- pipe
- product
- reduce
- reverse
- signum
- sort
- sort_by
- sort_with
- subtract
- sum
- tail
- tap
- true
- xor
abs :: a -> a
Returns the absolute value of an argument.
Daitai.abs.(11) # => 11
Daitai.abs.(-8) # => 8
add :: a -> a -> a
Calculates the sum of two arguments.
Daitai.add.(3, 4) # => 7
all :: (a -> Bool) -> [a] -> Bool
Checks if all elements of the list satisfy the predicate.
even = ->(x) { x % 2 == 0 }
Daitai.all.(even, [2, 4, 6, 8]) # => true
Daitai.all.(even, [2, 4, 7, 8]) # => false
always :: a -> b -> a
Creates a function that always returns the provided value.
always_zero = Daitai.always.(0)
always_zero.(:one) # => 0
always_zero.(7, 8) # => 0
Daitai.map.(always_zero, [1, 2, 3, 4]) # => [0, 0, 0, 0]
and :: Bool -> Bool -> Bool
Boolean and
- returns true
if both arguments are true. Otherwise returns false
.
Daitai.and.(true, true) # => true
Daitai.and.(true, false) # => false
Daitai.and.(false, true) # => false
Daitai.and.(false, false) # => false
any :: (a -> Bool) -> [a] -> Bool
Checks if at least one element of the list satisfies the predicate.
even = ->(x) { x % 2 == 0 }
Daitai.any.(even, [1, 2, 3, 5]) # => true
Daitai.any.(even, [1, 3, 5, 7]) # => false
comparator :: (a -> b -> Boolean) -> (a -> b -> Numeric)
Creates a comparator function based on a function which checks if the first argument is greater than the second one.
apple = { colour: 'red', weight: 136 }
banana = { colour: 'yellow', weight: 118 }
pear = { colour: 'green', weight: 178 }
weight_comparator = Daitai.comparator.(->(a, b) { a[:weight] > b[:weight]})
by_weight_decreasingly = Daitai.sort_with.(weight_comparator)
by_weight_decreasingly.([apple, banana, pear]) # => [pear, apple, banana]
compose :: (b -> c) -> (a -> b) -> (a -> c)
Applies one function to the result of another to produce a new function.
add_two = ->(x) { x + 2 }
square = ->(x) { x * x }
f = Daitai.compose.(square, add_two)
f.(10) # => 144
concat :: [a] -> [a] -> [a]
Returns the result of concatenating provided lists or strings.
Daitai.concat.([1, 2], [3, 4]) # => [1, 2, 3, 4]
Daitai.concat.("Szcz", "ecin") # => "Szczecin"
cond :: [[(*… → Bool), (*… → *)]] → (*… → *)
Takes a list of pairs consisted of a predicate and a transformer and returns a function which finds the first passing predicate and evaluates the corresponding transformer. Returns a nil
if there is no matching predicate.
function = Daitai.cond.(
[Daitai.is.(String), Daitai.always.("It's a String!")],
[Daitai.is.(Symbol), Daitai.always.("It's a Symbol!")],
[Daitai.true, ->(unknown) { "I don't know what #{unknown} is."}]
)
function.("いただきます") # => "It's a String!"
function.(:env) # => "It's a Symbol!"
function.(3.14) # => "I don't know what 3.14 is."
dec :: Numeric -> Numeric
Returns the decremented value of a provided number.
Daitai.dec.(7) # => 6
divide :: a -> a -> a
Calculates the quotient of two arguments.
Daitai.divide.(18, 6) # => 3
equals :: a -> b -> Bool
Returns true
if both arguments are equal. Otherwise returns false
.
Daitai.equals.(7, 7) # => true
Daitai.equals.('7', 7) # => false
Daitai.equals.(%w[a b c], %w[a b c]) # => true
false :: * -> Bool
Returns a function that ignores all arguments and always returns false
.
Daitai.false.() # => false
Daitai.false.(1, 2, 3) # => false
filter :: (a -> Bool) -> [a] -> [a]
Returns a list of all elements that satisfy the predicate.
greater_than_two = ->(x) { x > 2 }
Daitai.filter.(greater_than_two, [1, 2, 3, 4]) # => [3, 4]
Daitai.filter.(greater_than_two, x: 2, y: 3, z: 5) # => { y: 3, z: 5 }
only_even = Daitai.filter.(->(x) { x % 2 == 0 })
only_even.([1, 2, 3, 4]) # => [2, 4]
flip :: (a -> b -> … -> c) -> b -> a -> … -> c
Returns a copy of a function with reversed order of the first two arguments.
concat = ->(x, y) { x + y }
flipped_concat = Daitai.flip.(concat)
flipped_concat.("flip", "flop") # => "flopflip"
a -> a -> Bool
Checks if the first argument is greater than the second one.
Daitai.gt.(7, 5) # => true
Daitai.gt.(40, 40) # => false
Daitai.gt.(3.1, 3.14) # => false
a -> a -> Bool
Checks if the first argument is greater than or equal to the second one.
Daitai.gte.(7, 5) # => true
Daitai.gte.(40, 40) # => true
Daitai.gte.(3.1, 3.14) # => false
head :: [a] -> a
Returns the first element of a list.
Daitai.head.([1, 2, 3, 4]) # => 1
Daitai.head.("Ruby") # => "R"
identity :: a -> a
Returns exactly the provided value.
Daitai.identity.(1) # => 1
Daitai.identity.("Ruby") # => "Ruby"
inc :: Numeric -> Numeric
Returns the incremented value of a provided number.
Daitai.inc.(7) # => 8
init :: [a] -> [a]
Returns all the elements of a list except the last one.
Daitai.init.([1, 2, 3, 4]) # => [1, 2, 3]
Daitai.init.("Ruby") # => "Rub"
is :: Constant -> a -> Bool
Checks if an argument is an instance of the provided type.
Daitai.is.(Numeric, 7.77) # => true
Daitai.is.(Float, 7.77) # => true
Daitai.is.(String, "Ruby") # => true
Daitai.is.(Regexp, /hello/) # => true
Daitai.is.(Hash, {}) # => true
Daitai.is.(Enumerable, {}) # => true
Daitai.is.(Object, {}) # => true
Daitai.is.(Numeric, {}) # => false
is_nil :: a -> Bool
Checks if an argument is a nil
.
Daitai.is_nil.(nil) # => true
Daitai.is_nil.(false) # => false
Daitai.is_nil.(0) # => false
last :: [a] -> a
Returns the last element of a list.
Daitai.last.([1, 2, 3, 4]) # => 4
Daitai.last.("Ruby") # => "y"
length :: [a] -> Integer
Returns the length of a list.
Daitai.length.([1, 2, 3, 4]) # => 4
Daitai.length.("Ruby") # => 4
lt :: a -> a -> Bool
Checks if the first argument is less than the second one.
Daitai.lt.(5, 7) # => true
Daitai.lt.(40, 40) # => false
Daitai.lt.(3.14, 3.1) # => false
lte :: a -> a -> Bool
Checks if the first argument is less than or equal to the second one.
Daitai.lte.(5, 7) # => true
Daitai.lte.(40, 40) # => true
Daitai.lte.(3.14, 3.1) # => false
map :: (a -> b) -> [a] -> [b]
Applies the function to all elements of the list and returns a new list of the results.
triple = ->(x) { x * 3 }
Daitai.map.(triple, [1, 2, 3, 4]) # => [3, 6, 9, 12]
Daitai.map.(triple, a: 10, b: 13) # => { a: 30, b: 39 }
increment = Daitai.map.(->(x) { x + 1 })
increment.([1, 2, 3, 4]) # => [2, 3, 4, 5]
mean :: [Numeric] -> Float
Returns the mean of a list.
Daitai.mean.([3, 4.5, 9]) # => 5.5
Daitai.mean.([6, 7]) # => 6.5
Daitai.mean.([]) # => NaN
median :: [Numeric] -> Float
Returns the median of a list.
Daitai.median.([3.14, 4.5, 7.77]) # => 4.5
Daitai.median.([6, 7]) # => 6.5
Daitai.median.([]) # => NaN
max :: a -> a -> a
Returns the larger of two arguments.
Daitai.max.(6, 7) # => 7
non_negative = Daitai.max.(0)
non_negative.(-7) # => 0
non_negative.(11) # => 11
min :: a -> a -> a
Returns the smaller of two arguments.
Daitai.min.(6, 7) # => 6
non_positive = Daitai.min.(0)
non_positive.(-7) # => -7
non_positive.(11) # => 0
modulo :: a -> a -> a
Calculates the remainder after division of two arguments.
Daitai.modulo.(18, 7) # => 4
multiply :: a -> a -> a
Calculates the product of two arguments.
Daitai.multiply.(4, 3) # => 12
negate :: a -> a
Unary negation - returns a negated value of the argument.
Daitai.negate.(11) # => -11
Daitai.negate.(-8) # => 8
not :: Bool -> Bool
Boolean not
- returns a contradiction of the argument.
Daitai.not.(true) # => false
Daitai.not.(false) # => true
Daitai.not.('λ') # => false
Daitai.not.(nil) # => true
once :: (a -> … -> b) -> (a -> … -> b)
Returns a wrapped function which can be executed only once - no matter how many times it is called.
decrement = ->(x) { x - 1 }
decrement_once = Daitai.once.(decrement)
decrement_once.(8) # => 7
decrement_once.(40) # => 7
decrement_once.(decrement_once.(40)) # => 7
or :: Bool -> Bool -> Bool
Boolean or
- returns true
if at least one of the arguments is true. Otherwise returs false
.
Daitai.or.(true, true) # => true
Daitai.or.(true, false) # => true
Daitai.or.(false, true) # => true
Daitai.or.(false, false) # => false
partition :: (a -> Bool) -> [a] -> [[a], [a]]
Returns a pair of lists of elements that do and do not satisfy the predicate.
greater_than_two = ->(x) { x > 2 }
Daitai.partition.(greater_than_two, [1, 2, 3, 4]) # => [[3, 4], [1, 2]]
Daitai.partition.(greater_than_two, x: 2, y: 3, z: 5) # => [{ y: 3, z: 5 }, { x: 2 }]
partition_numbers = Daitai.partition.(->(x) { x % 2 == 0 })
partition_numbers.([1, 2, 3, 4]) # => [[2, 4], [1, 3]]
pipe :: (a -> b) -> (b -> c) -> (a -> c)
Performs a function composition from left to right and returns a new function.
add_two = ->(x) { x + 2 }
square = ->(x) { x * x }
f = Daitai.pipe.(square, add_two)
f.(10) # => 102
product :: [a] -> a
Calculates the product of all elements of a list.
Daitai.sum.([1, 2, 3, 4]) # => 24
reduce :: (a -> b -> a) -> a -> [b] -> a
Reduces the list using the function, from left to right, using the accumulator.
add = ->(x, y) { x + y }
Daitai.reduce.(add, 0, [1, 2, 3, 4]) # => 10
sum = ->(acc, (_, v)) { v + acc }
Daitai.reduce.(sum, 0, x: 2, y: 3, z: 5) # => 10
concat = Daitai.reduce.(add, "")
concat.(%w[l a m b d a]) # => "lambda"
reverse :: [a] -> [a]
Returns the elements of a list in reverse order.
Daitai.reverse.([0, 5, 10, 15]) # => [15, 10, 5, 0]
Daitai.reverse.("raw desserts") # => "stressed war"
signum :: a -> a
Extracts the sign of an argument.
Daitai.signum.(11) # => 1
Daitai.signum.(0) # => 0
Daitai.signum.(-8) # => -1
sort :: [a] -> [a]
Returns a copy of the list sorted in the ascending order.
Daitai.sort.(diff, [2, 1, 4, 3]) # => [1, 2, 3, 4]
Daitai.sort.(%w[haskell ruby elixir]) # => ["elixir", "haskell", "ruby"]
sort_by :: a -> [a] -> [a]
Returns a copy of the list sorted by the provided property - either a key of a Hash
or a name of an Object
's function.
apple = { colour: 'red', weight: 136 }
banana = { colour: 'yellow', weight: 118 }
pear = { colour: 'green', weight: 178 }
Daitai.sort_by.(:weight, [apple, banana, pear]) # => [banana, apple, pear]
sort_by_length = Daitai.sort_by.(:length)
sort_by_length.(%w[haskell ruby elixir] # => ["ruby", "elixir", "haskell"]
sort_with :: (a -> a -> Numeric) -> [a] -> [a]
Returns a sorted copy of the list according to the specified comparator function.
diff = ->(x, y) { x - y }
Daitai.sort.(diff, [2, 1, 4, 3]) # => [1, 2, 3, 4]
sort_by_length = Daitai.sort.(->(x, y) { x.length - y.length })
sort_by_length.(%w[haskell ruby elixir]) # => ["ruby", "elixir", "haskell"]
subtract :: a -> a -> a
Calculates the differce of two arguments.
Daitai.subtract.(9, 4) # => 5
sum :: [a] -> a
Calculates the sum of all elements of a list.
Daitai.sum.([1, 2, 3, 4]) # => 10
tail :: [a] -> [a]
Returns all the elements of a list except the first one.
Daitai.tail.([1, 2, 3, 4]) # => [2, 3, 4]
Daitai.tail.("Ruby") # => "uby"
tap :: (a -> b) -> a -> a
Executes the given function with the provided argument, then returns the argument.
logger = ->(x) { puts "the value is #{x}" }
Daitai.tap.(logger, 7)
# the value is 7
# => 7
true :: * → Bool
Returns a function that ignores all arguments and always returns true
.
Daitai.true.() # => true
Daitai.true.(1, 2, 3) # => true
xor :: Bool -> Bool -> Bool
Boolean xor
- returns true
if only one of the arguments is true. Otherwise returs false
.
Daitai.xor.(true, true) # => false
Daitai.xor.(true, false) # => true
Daitai.xor.(false, true) # => true
Daitai.xor.(false, false) # => false
Development
After checking out the repo, run bin/setup
to install dependencies. Then, run rake spec
to run the tests. You can also run bin/console
for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install
. To release a new version, update the version number in version.rb
, and then run bundle exec rake release
, which will create a git tag for the version, push git commits and tags, and push the .gem
file to rubygems.org.
Contributing
Bug reports and pull requests are welcome on GitHub at walerian777/daitai. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.
License
The gem is available as open source under the terms of the MIT License.
Code of Conduct
Everyone interacting in the Daitai project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.