Skip to content

Instantly share code, notes, and snippets.

@dminuoso
Last active November 6, 2017 23:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save dminuoso/07827d599de13e004be2007ad1a65074 to your computer and use it in GitHub Desktop.
Save dminuoso/07827d599de13e004be2007ad1a65074 to your computer and use it in GitHub Desktop.
# Identity functor that does not give any additional structure
Identity = Struct.new(:value) do
def fmap(func = nil, &blk)
f = func || blk;
Identity.new(f.call(self.value))
end
end
# Const Functor that preserves its content
Const = Struct.new(:value) do
def fmap(f = nil, o = nil)
self
end
# A curried constructor for sanity.
def self.make(*args)
-> (o) { new(o) }.curry[*args]
end
end
class Object
def deep_dup
dup
end
end
class Hash
def deep_dup
hash = dup
each_pair do |key, value|
if key.frozen? && ::String === key
hash[key] = value.deep_dup
else
hash.delete(key)
hash[key.deep_dup] = value.deep_dup
end
end
hash
end
end
class Proc
def *(other)
-> (*args) { self[other[*args]] }
end
end
class Array
def deep_dup
map(&:deep_dup)
end
def fmap(*args)
-> (fun, e) {
e.map(&fun)
}.curry[*args]
end
def self.nth(*args)
-> (n) {
-> (o) { o[n] }
}.curry[*args]
end
def self.lens_index(*args)
-> (n) {
L.ens(nth(n), update(n))
}.curry[*args]
end
def self.update(*args)
-> (n, v, array) {
array.deep_dup.tap { |a| a[n] = v }
}.curry[*args]
end
end
module Kernel
def fmap(*args)
-> (fn, e) {
e.class.instance_method(:fmap).bind(e).call(fn)
}.curry[*args]
end
end
def const(*args)
-> (val) {
-> (_) { val }
}.curry[*args]
end
module L
class << self
def ens(*args)
-> (g, s, w, t) {
fmap(-> (f) {
s.(f, t)
}, w.(g.(t)))
}.curry[*args]
end
def over(*args)
-> (lens, fun, obj) {
lens.(-> (y) {
Identity.new(fun.(y))
}).(obj).value
}.curry[*args]
end
def view(*args)
-> (lens, obj) {
lens.(Const.make).(obj).value
}.curry[*args]
end
def set(*args)
-> (lens, val, obj) {
over(lens, const(val), obj)
}.curry[*args]
end
end
end
class Hash
def self.getter(*args)
-> (paths, obj) {
obj.dig(*paths)
}.curry[*args]
end
def self.setter(*args)
-> (paths, val, obj) {
obj.deep_dup.tap do |o|
*dir, final = paths
if dir.empty?
o.store(final, val)
else
o.dig(*dir).store(final, val)
end
end
}.curry[*args]
end
def self.lens_path(*args)
-> (paths) {
L.ens(getter(paths), setter(paths))
}.curry[*args]
end
end
profile = {
username: 'dminuoso',
details: {
ability: 'writing functions',
},
friend: {
name: 'baweaver',
details: {
ability: 'puns',
}
}
}
friend_l = Hash.lens_path([:friend])
ability_l = Hash.lens_path([:details, :ability])
upcase = -> (e) { e.upcase }
puts L.view(
friend_l * ability_l,
profile
)
puts L.set(
ability_l,
'composing functions',
profile
)
puts L.over(
ability_l,
upcase,
profile
)
puts L.over(
friend_l * ability_l,
upcase,
profile
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment