Project

literally

0.0
No release in over 3 years
A tiny pre-processor for ruby that adds runtime type checking to methods.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
 Dependencies

Runtime

 Project Readme

Literally [WIP]

Literally is a tiny pre-processor for Ruby that lets you write methods with runtime type checking using Literal.

Literally uses genuine Ruby syntax but pre-processes it to be interpreted in a different way.

Return types

You can specify a return type for a method.

def say_hello = String do
  "Hello World!"
end

This will add a runtime assertion that the return value is a String.

Note

The return value must be specified for Literally to process the method at all. You can always specify that it returns _Void or _Any?.

Positional arguments

We use Ruby’s default value for arguments as the place to specify the type.

def say_hello(name = String) = String do
  "Hello #{name}!"
end

Okay, but how do we specify defaults?

def say_hello(name = String {"World"}) = String do
  "Hello #{name}!"
end

Keyword arguments

Keyword arguments work the same way

def say_hello(name: String {"World"}) = String do
  "Hello #{name}!"
end

Splats

Splats are typed by using an Array literal

def say_hello(names = [String]) = String do
  "Hello #{names.join(", ")}!"
end

This is the equivalent to def say_hello(*names). In this context [String] makes the *names splat and types it as _Array(String).

You don’t need to specify a default because splats always default to an empty Array.

But what if you wanted to use the literal value [String] itself as a type? [String] is in fact a type that only matches an array that contains the String class.

As you can see

[String] === [String]

In this case, you can wrap the type in parentheses

def say_hello(names = ([String])) = String do
  # the positional argument `names` here must be
  # an array with the class `String` and nothing else inside it.
end

Keyword splats

Keyword splats are the same but using a Hash literal to specify K/V types. In this context, {String => String} makes the **greetings keyword splat and types it as _Hash(String, String).

def say_hello(greetings: {String => String}) = String do
  greetings.map do |greeting, name|
    "#{greeting} #{name}"
  end.join("\n")
end

Again, defaults don’t need to be specified here because the default will always be an empty Hash.

Splats with named types

Let’s say you have a type called Position.

Position = _Tuple(Integer, Integer, Integer)

And you want to accept *position as Position, you could specify that like this

def move_to(position = [*Position]) = _Void do
end

Same with keyword arguments and keyword splats

Position = _Map(x: Integer, y: Integer, z: Integer)
def move_to(position: {**Position}) = _Void do
end

Blocks

Blocks are always optional and always Procs so there’s no reason to type them. If you want to require a block, we recommend adding raise unless block_given? to your method.