Skip to content

Instantly share code, notes, and snippets.

@baweaver
Last active May 24, 2022 07:34
Show Gist options
  • Save baweaver/fa4236ad53373448d4aba27f12e7c2b1 to your computer and use it in GitHub Desktop.
Save baweaver/fa4236ad53373448d4aba27f12e7c2b1 to your computer and use it in GitHub Desktop.
Quick musing on ADT in Ruby using Sorbet, name TBD. May gemify it later.
require "sorbet-runtime"
module ADT
module DeconstructableSorbetStruct
def deconstruct
self.class.props.map { send(_1) }
end
def deconstruct_keys(keys)
return self.class.props.to_h { |k| [k, send(k)] }
(self.class.props & keys).to_h { |k| [k, send(k)] }
end
end
def self.type(**config, &fn)
Class.new(T::Struct) do
include DeconstructableSorbetStruct
config.each do |field, type|
const field, type
end
module_eval(&fn) if block_given?
end
end
def self.sum(**types, &fn)
container_module = Module.new do
extend T::Helpers
sealed!
module_eval(&fn) if block_given?
end
types.each do |type, config|
klass = Class.new(T::Struct) do
include container_module
include DeconstructableSorbetStruct
config.each do |field, type|
const field, type
end
end
container_module.const_set(type.capitalize, klass)
end
container_module
end
end
Coord = ADT.type(x: Integer, y: Integer, z: Integer)
Shape = ADT.sum(
circle: { radius: Integer, center: Coord },
square: { top_left: Coord, bottom_right: Coord }
)
Coord = ADT.type(x: Integer, y: Integer, z: Integer) do
def translate(x: 0, y: 0, z: 0)
Coord.new(x: @x + x, y: @y + y, z: @z + z)
end
end
Shape = ADT.sum(
circle: { radius: Integer, center: Coord },
square: { top_left: Coord, bottom_right: Coord }
) do
def translate(x: 0, y: 0, z: 0)
case self
when Shape::Circle
Shape::Circle.new(radius:, center: center.translate(x:, y:, z:))
when Shape::Square
Shape::Square.new(
top_left: top_left.translate(x:, y:, z:),
bottom_right: bottom_right.translate(x:, y:, z:)
)
else
T.absurd(self)
end
end
end
center = Coord.new(x: 0, y: 0, z: 0)
# => <Coord x=0, y=0, z=0>
circle = Shape::Circle.new(radius: 5, center:)
# => <Shape::Circle center=<Coord x=0, y=0, z=0>, radius=5>
circle.translate(z: 10)
# => <Shape::Circle center=<Coord x=0, y=0, z=10>, radius=5>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment