Daitai Build Status Gem Version

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 :: 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

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.