On this page:
Liquid Shplait
1.1.0.2+9.4.0.2

Liquid Shplait🔗ℹ

Liquid Shplait implements “liquid” typing. Liquid types are a long line of work by Ranjit Jhala and collaborators, starting with this one. Liquid types are a form of refinement types, which were originally added to modern programming languages in this work. Here’s a more recent survey on the concept of refinement types. Liquid Shplait is directly inspired by and based on these ideas.

Liquid Shplait is a liquid extension to Shplait. A refinement attaches a predicate to a type. Thus, for instance, this example says that double takes only positive integers and returns positive integers:

#lang shplait/liquid

 

fun double({n :: Int | n > 0}) :: {y :: Int | y > 0}:

  n * 2

While the program compiles, the checker turns every such claim into proof obligations and asks the Z3 solver to discharge them. If the body were n / 2 or n - 2, the program would not compile:

  `double`, return: the claim `y > 0` is violated when n = 1

If, however, the function definition were changed to

#lang shplait/liquid

 

fun double({n :: Int | n > 2}) :: {y :: Int | y > 0}:

  n - 2

then it would again type-check without any difficulty.

Checking refinements happens purely statically. The checker does not add run-time checks: any refinement that cannot be checked is rejected, not turned into a run-time check. Checking happens as part of type-checking, so if the program compiles (e.g., in DrRacket you see the REPL prompt), that means all the checks have passed.

 #lang shplait/liquid package: liquid-shplait

    1 Installing Liquid Shplait

    2 Running Programs

    3 Refinements

    4 What Liquid Shplait Proves

    5 Rejections

    6 Datatypes

    7 Options, Pairs, and Characters

    8 Measure Functions

    9 Checked Bodies

    10 error, unreachable, and assert

    11 Polymorphic Functions

    12 Mutable State

    13 Checks at the Boundary

    14 The Predicate Language

    15 Timeout

    16 Limitations

    17 Examples