Skip to content

Instantly share code, notes, and snippets.

@mklbtz
Created May 21, 2018 22:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mklbtz/3db2932ad2a4b3d014e469a6e655df04 to your computer and use it in GitHub Desktop.
Save mklbtz/3db2932ad2a4b3d014e469a6e655df04 to your computer and use it in GitHub Desktop.
Demonstrating the "final tagless" approach to the "Expression Problem" in Ruby
class View
def self.image(url:)
Image.new.tap { |img| img.url = url }
end
def self.button(title:)
Button.new.tap { |btn| btn.title = title }
end
def self.stack(subviews)
Stack.new.tap { |stk| stk.subviews = subviews }
end
def self.text(text)
Text.new.tap { |txt| txt.text = text }
end
def self.map(location:)
Map.new.tap { |map| map.location = location }
end
end
class Image < View
attr_accessor :url
end
class Button < View
attr_accessor :title
end
class Stack < View
attr_accessor :subviews
end
class Text < View
attr_accessor :text
end
class Map < View
attr_accessor :location
end
class Logger
def self.image(url:)
"An image named #{url.inspect}"
end
def self.button(title:)
"[ #{title} ]"
end
def self.stack(subviews)
indent = -> (str) { str.split("\n").map { |line| " #{line}" }.join("\n") }
"A stack containing...\n" +
subviews.map(&indent).join("\n")
end
def self.text(text)
text.inspect
end
def self.map(location:)
"A map located #{location.inspect}"
end
end
def full_view(i)
i.stack([
i.image(url: 'icon.png'),
i.map(location: 'here'),
i.stack([
i.button(title: 'Like'),
i.button(title: 'Pass')
])
])
end
full_view(View)
# => #<Stack:0x007fc4341d0ce0 @subviews=[#<Image:0x007fc4341d0e98 @url="icon.png">, #<Map:0x007fc4341d0e20 @location="here">, #<Stack:0x007fc4341d0d30 @subviews=[#<Button:0x007fc4341d0dd0 @title="Like">, #<Button:0x007fc4341d0d80 @title="Pass">]>]>
puts full_view(Logger)
# >> A stack containing...
# >> An image named "icon.png"
# >> A map located "here"
# >> A stack containing...
# >> [ Like ]
# >> [ Pass ]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment