F*

File size
12.8KB
Lines of code
315

F*

Functional language for writing formal verification of programs.

Comments

// ----- COMMENT -----

// this is a single-line comment

(* this is a 
multi-line 
comment *)

Printing

// ----- PRINTING -----
    // print_string => receives a string argument that is then printed to the stdout with no inclusion of a newline by default
    // note that there is no default implementation for a print function that automatically includes a newline

let _ = print_string "this does not include a newline"
let _ = print_string "this has a newline but only because we explicitly specified it's inclusion\n"

Quickstart

// ----- QUICKSTART -----
    // strong formal verification capabilities through specification of preconditions, postconditions and invariants allow writing of formal proofs to verify program correctness
    // modular programming with namespaces
    // various transpilation targets including OCaml, C and WASM
    // module => defines a module used to organise code, required as a module definition at the beginning of every F* file, the equivalent of namespaces in other languages
    // let => begins variable or function declaration within a given local scope, alongside being used for pattern-matching
        // note that values within F* are IMMUTABLE by default unless explicitly specified otherwise, a hallmark of the functional paradigm
    // : => declares the type of a value stored within a variable, providing type declaration within F*
    // _ => placeholder, wildcard operator that represents a variable or module binding that is to be discarded, as well as affording anonymous immediate function calls

module Quickstart

let add (x:int) (y:int) : Tot int =
    x + y

let _ =
    let result = add 3 4 in
    print_string (string_of_int result)

let (_, onlyKeepThisValue) = (1, 2) 
let unusedVarExample = _ 
module _ = anExampleModuleToBeDiscarded

Types

// ----- TYPE -----
    // int => stores an integer number value
    // float => stores a floating number value
    // string => stores a string value declared within "" double quotation marks
    // bool => true, false

let anExampleInt : int = 42
let anExampleFloat : float = 3.14
let anExampleString : string = "Hello, F*!"
let anExampleBool : bool = true

Operators

// ----- OPERATOR -----

// --- ARITHMETIC OPERATOR ---

+ // addition
- // subtraction
* // multiplication
/ // divison
% // modulo

// --- COMPARISON OPERATOR ---

= // complete equality check for both type and value
<> // complete inequality check for both type and value
< // comparison operator
> // comparison operator
<= // comparison operator
>= // comparison operator

// --- LOGICAL OPERATOR ---

&& // logical and
|| // logical or
not // logical not

Control structures

// ----- CONTROL STRUCTURE =====

// --- CONDITIONALS ---

// IF THEN ELSE IF THEN ELSE
    // note the use of then to suffix every predicate condition following the if and else if statements similar to Bash
    // the result of a conditional statement can be directly assigned as a function definition or a value as below, a hallmark of the functional paradigm

let classify_number (n:int) : string =
    if n < 0 then
        "Negative"
    else if n = 0 then
        "Zero"
    else
        "Positive"

// MATCH WITH |
    // the equivalent of match case statements in other programming languages, providing powerful pattern matching capabilities in F* similar to Rust
    // match with => specifies the predicate variable to be matched
    // | => prefixes every possible matching predicate case condition 

type shape =
    | Circle of float 
    | Rectangle of float * float
    | Triangle of float * float * float 

let describe_shape (s:shape) : string =
    match s with
        | Circle r -> Printf.sprintf "Circle with radius %f" r
        | Rectangle (w, h) -> Printf.sprintf "Rectangle with width %f and height %f" w h
        | Triangle (a, b, c) -> Printf.sprintf "Triangle with sides %f, %f, %f" a b c

// --- LOOPS ---
    // as a functional programming language, F* does not provide conventional loop constructs
    // instead, higher-order functions and recursion are two ways in which iteration and traversal of iterable data structures can be achieved in F*

// HIGHER-ORDER FUNCTIONS
    // note that F* itself does not provide a default implementation for common higher-order functions like map, filter, lfold and rfold
    // however, first-class functions mean that the programmer can easily implement these themselves
    // the below is merely one way to implement these higher order functions, and there are many alternative methods to achieve the same effect
    // in fact, many higher-order function implementations in F* are also recursive, as seen below

let rec map (f:'a -> 'b) (lst:list 'a) : list 'b =
    match lst with
        | [] -> []
        | x::xs -> f x :: map f xs

let rec filter (p:'a -> bool) (lst:list 'a) : list 'a =
    match lst with
        | [] -> []
        | x::xs -> if p x then x :: filter p xs else filter p xs

let rec lfold (f:'b -> 'a -> 'b) (acc:'b) (lst:list 'a) : 'b =
    match lst with
        | [] -> acc
        | x::xs -> lfold f (f acc x) xs

let rec rfold (f:'a -> 'b -> 'b) (acc:'b) (lst:list 'a) : 'b =
    match lst with
        | [] -> acc
        | x::xs -> f x (rfold f acc xs)

// RECURSION
    // rec => specifies that a given function contains a recursive call

let rec factorial n =
    if n = 0 then 1
    else n * factorial (n - 1)

Data structures

// ----- DATA STRUCTURE -----
    // list => ordered collection of ; semicolon-delimited elements of the same datatype, declared within [] square brackets
    // tuple => ordered collection of , comma-delimited elements of multiple datatypes, declared within () round brackets wherein each tuple's type signature is a composite unique datatype
        // note that the type declaration of a tuple sees its elements' datatypes being * asterisk-delimited
    // record => user-defined collection of named fields and their respective datatypes, declared within {} curly braces, effectively allowing modelling of representative data through type aliases
    // algebraic datatype => ordered collection of | pipe-delimited constructor datatypes that can store multiple types of values
        // of => used to specify the datatype of each possible form within the algenbraic datatype
    // option => nullable datatype that specifies a given variable might either store the special value Some or the null-equivalent value None 
        // Some => specifies the presence of a value
        // None => specifies the absence of a value
    // result => stores a value that is either the special value Ok or the special value Error
        // Ok => succesful result
        // Error => error was hit 
    // variant => ordered collection of named | pipe-delimited constructors each holding values of different types

let anExampleList : list int = [1; 2; 3; 4]
let anExampleTuple : (int * string * bool) = (1, "one", true)

type anExampleRecord : {
    name : string;
    age : int;
}

type threeDimensionalCoordinate {
    X : int;
    Y : int;
    Z : int;
}

type anExampleAlgebriacDatatype =
    | Circle of float
    | Rectangle of float
let myCircle : anExampleAlgebriacDatatype = Circle 3.0
let myRectangle : anExampleAlgebriacDatatype = Rectangle (3.0, 4.0)

let anExampleOptionValue1 : option int = Some 42
let anExampleOptionValue2 : option int = None

type anExampleResultValue 'a 'e =
    | Ok of 'a
    | Error of 'e
let successful : anExampleResultValue int string = Ok 42
let failed : anExampleResultValue int string = Error "Something went wrong"

type anExampleVariantValue =
    | IntValue of int
    | StringValue of string
    | BoolValue of bool
let variantExample : anExampleVariantValue = IntValue 42

Functions

// ----- FUNCTION -----
    // functions in F* are first-class citizens, allowing for the creation of higher-order functions, a hallmark of the functional paradigm
    // similar to many other functional languages, F* functions also feature implicit return of the last expression within the function
    // as previously mentioned above, F* features an extremely strong type system, seem below in the multiple function annotations within a single function definition
    // let <functionAnnotation1> <functionName> ( <functionParameterName(s)> : <functionParamterDatatype(s)> ) : <functionAnnotation2> <returnValueDatatype> = <functionDefinitionBody> => declaration and definition of a named function

// --- FUNCTION ANNOTATION ---

// FUNCTION ANNOTATION 1
    // rec => specifies that a given function contains a recursive call
    // ghost => specifies that a given function is intended for verification purposes only and should not be used in proper runtime execution
    // lemma => specifies that a given function is meant as an intermediate step in a formal proof and should not be used in proper runtime execution

// FUNCTION PARAMETER DATATYPE
    // Decrease => within recursive function calls, function parameter datatypes(s) can sometimes be specified as "Decrease" where a metric decreases with each recursive call, ensuring further guarantee of termination of a recursive function
        // in these cases, the function annotation 2 of the function would be "Tot" since there is certainty of termination of the function with no possibility of an infinite occuring

// FUNCTION ANNOTATION 2
    // Tot => specifies that a given function will terminate with the specified return datatype and will not enter an infinite loop
    // !Tot => specifies that a given function might not terminate and resultingly might not produce a result for certain inputs
    // Erased => specifies that a given function should be ignored and erased during compilation of the F* file, meaning it does not have any runtime representation and thus is used for static checks and proofs only

let add (x:int) (y:int) : Tot int =
    x + y

let rec div_by_zero (x:int) : !Tot int =
    1 / (x - 1)

let ghost function_for_verification (x:int) : Tot int =
    x + 1

let lemma example_lemma (x:int) : Tot int =
    x + 1

let rec factorial (x:int) (decr:Decrease x) : Tot int =
    if x = 0 then 
        1
    else 
        x * factorial (x - 1) (decr x)

let erased_function (x:int) : Erased int =
    x + 1

More on