Skip to content

yippee-fun/literally

Repository files navigation

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.

About

No description, website, or topics provided.

Resources

License

Code of conduct

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •  

Languages