Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

safe_and_sound

Package Overview
Dependencies
Maintainers
1
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

safe_and_sound

  • 0.3.0
  • Rubygems
  • Socket score

Version published
Maintainers
1
Created
Source

Unit tests

Safe and Sound: Sum Data Types and utilities for Ruby

This library gives you and alternative syntax to declare new types/classes. It's inspired by the concise syntax to declare new types in Elm or Haskell These types share some properties with types referred to as algebaric data types, sum types or union types.

We can model similar relationships more verbosely in plain Ruby with classes and subclasses. This library provides some syntactic shortcuts to create these hierarchies.

Vehicle = SafeAndSound.new(
    Car: { horsepower: Integer },
    Bike: { gears: Integer}
  )

This will create an abstract base class Vehicle. Instances can only be created for the concrete subclasses Car or Bike. The class names act as "constructor" functions and values created that way are immutable.

car = Vehicle.car(horsepower: 100)
puts car.horsepower # 100

bike = Vehicle.bike(gears: 'twentyone')
# SafeAndSound::WrgonConstructorArgType (gears must be of type Integer but was String)

nil is not a valid constructor argument. Optional values can be modeled with the Maybe type that is also provided with the library.

To add polymorphic behavior we can write functions without having to touch the new types themselves.

Safe, polymorphic functions

By including the SafeAndSound::Functions module we get access to the chase function. It immitates the case statement but uses the knowledge about our types to make it more safe.

include SafeAndSound::Functions

def to_human(vehicle)
  chase vehicle do
    wenn Vehicle::Car,  -> { "I'm a car with #{horsepower} horsepower" }
    wenn Vehicle::Bike, -> { "I'm a bike with #{gears} gears" }
  end
end

This offers a stricter version of the case statement. Specifically it makes sure that all variants are handled (unless an otherwise block is given). This check will still be only performed at runtime, but as long as there is at least one test executing this chase expression we'll get an early, precise exception telling us what's missing.

If you want a more detailed explanation why working with such objects can be appealing I recommend you watch the Functional Core, Imperative Shell episode of the Destroy all software screencast.

I'm not trying to change how Ruby code is written. This is merely an experiment how far the sum type concept can be taken in terms of making a syntax for it look like the syntax in languages where this concept is more central.

Check out more examples in the examples folder.

JSON serialization/deserialization included

irb(main)> car = Vehicle.Car(horsepower: 100)

irb(main)> car.as_json # converts to a Hash of primitives
=> {"type"=>"Car", "horsepower"=>100}

irb(main)> puts car.to_json # converts to actual JSON string
{"type":"Car","horsepower":100}

irb(main)> Vehicle.from_hash({"type"=>"Car", "horsepower"=>100})
=> #<Vehicle::Car:0x000000010ef49a48 @horsepower=100>

irb(main)> Vehicle.from_json('{"type":"Car","horsepower":100}')
=> #<Vehicle::Car:0x000000010ef6ae00 @horsepower=100>

FAQs

Package last updated on 22 Apr 2022

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc