/necromancy

屍術/Necromancy conjures up the functional code.

Primary LanguageRubyMIT LicenseMIT

屍術/Necromancy

屍術/Necromancy conjures up the functional code.

require 'necromancy'
N = Necromancy.new

# [:foo, :bar, :baz].map{|s| s.to_s }.map{|s| s.upcase }
[:foo, :bar, :baz].map &N.to_s . upcase

# [:foo, :hoge, :bar, :fuga].select{|s| s.to_s.length > 3} # => [:hoge, :fuga]
[:foo, :hoge, :bar, :fuga].select &N.to_s . length > 3

Installation

gem install necromancy

Features

Function composition

Every messages to instance of Necromancy are function composition by default. that is left-to-right composition.

N.f.g == ->(o) { :g.to_proc(:f.to_proc(o)) } == ->(o) { o.f.g }

Application with arguments

If a message was called with some argument given, their arguments are given into that function each time.

N.f(x) == ->(o) { :f.to_proc(o, x) } == ->(o) { o.f(x) }

Rich extensions

If you want, you can use extensions by clojuring up the evil spirit.

M = Necromancy.Alternative.new
M.x | M.y == ->(o) { o.x || o.y }

No core extensions

Open classes is evil unless that is need really! 屍術/Necromancy isn't. All methods are defining at local modules, and you can call their methods by sending some messages to a Necromancy object.

Influenced by Haskell

屍術/Necromancy influenced by Haskell. Practically, the library provides Haskell's syntax for Ruby.

require 'necromancy'
N = Necromancy.Alternative.new

f = lambda(&N >> N + 1)
f.(42)  # => 43
f.(nil) # => nil
import Control.Applicative

f n = (+1) <$> n
f (Just 42) -- Just 43
f Nothing   -- Nothing

img/rbhs.png

Illustrated by @chomado

Examples

Simple function composition

First, you create a Necromancy object. it is immutable, you can save it to any variable you like. for example, that is constant, global varibale, instance variable, class variable, local variable, etc.

N = Necromancy.new

After, you send some message to N when you need to write a simple block.

(1..5).map &N ** 2 # => [1, 4, 9, 16, 25]

Function composition

N = Necromancy.Category.new
ary = ('A'..'Z').to_a
(0..4).map &N > ary.method(:[]) # => ["A", "B", "C", "D", "E"]

Multiple accessing to attribtues

N = Necromancy.Arrow.new
str = "foo"
lambda(&N.upcase & :capitalize & :reverse).(str) # => ["FOO", "Foo", "oof"]

Maybe evaluating

N = Necromancy.Alternative.new
n = N >> N.upcase!
"foo".tap &n # => "FOO"
nil.tap &n # => nil

Alias importation

N = Necromancy.Alternative[:>> => :then].new
str_or_nil = ["foo", nil].sample
str_or_nil.tap &(N.then N.upcase!) # => nil or "FOO"

Hiding importation

N = Necromancy.Alternative.hiding(:*, :**).new
(1..5).map &N ** 2 # => [1, 4, 9, 16, 25]

Specifying importation

N = Necromancy.Alternative(:>>).new
str_or_nil = ["foo", nil].sample
str_or_nil.tap &N >> N.upcase! # => nil or "FOO"
(1..5).map &N ** 2 # => [1, 4, 9, 16, 25]

Multiple module importation

N = Necromancy.Arrow.Alternative.hiding(:*, :**).new
[nil, 42, "foo"].map &N.is_a?(Integer) >> (N * 2 & N ** 2) | N # => [nil, [84, 1764], "foo"]