home > software > hosa > index

Sized-type Inference for Higher-Order Functional Programs

Sized-type inference for higher-order functional programs

HoSA performs inference of sized-types for higher-order functional programs, as explained in our recent paper.

Download and installation

HoSA is open source and can be downloaded from its github page.

Requirements

HoSA is implemented in Haskell and relies on a recent version of the Glasgow Haskell Compiler.

Install

HoSA requires various auxiliary libraries and tools, such as our GUBS solver. Thus, the easiest way to install HoSA is via stack. To install HoSA via stack, navigate to the source distribution folder in a terminal and type:

stack install

This will install HoSA together with all its requirements. Note however that GUBS solver needs an SMT-solver installed, such as Microsoft’s Z3.

Usage

HoSA is invoked by the command line by

hosa <file>

where <file> is the functional program that should be analysed. For additional command line flags, type

hosa --help

In particular the flag -a time allows to switch on runtime complexity analysis via code-ticking. HoSA is still under development and features currently just a simple format for higher-order programs, see the example folder for the syntax of input programs.

An Example

> cat examples/product.atrs
foldr f b [] = b;
foldr f b (l : ls) = f l (foldr f b ls);

lam1 n m l = (n,m) : l;
lam2 ms n l = foldr (lam1 n) l ms;
product ns ms = foldr (lam2 ms) [] ns;

> hosa examples/product.atrs
Input program:
  foldr :: (β -> α -> α) -> α -> L(β) -> α
  foldr f b ((:) l ls) = f l (foldr f b ls)
  foldr f b [] = b

  lam1 :: β -> α -> L((β,α)) -> L((β,α))
  lam1 n m l = (:) (n,m) l

  lam2 :: L(α) -> β -> L((β,α)) -> L((β,α))
  lam2 ms n l = foldr (lam1 n) l ms

  product :: L(β) -> L(α) -> L((β,α))
  product ns ms = foldr (lam2 ms) [] ns

  where
    (:) :: α -> L(α) -> L(α)
    [] :: L(α)

Calling-context annotated program:
  foldr :: (γ -> L((β,α)) -> L((β,α))) -> L((β,α)) -> L(γ) -> L((β,α))
  foldr f b ((:) l ls) = f l (foldr f b ls)
  foldr f b [] = b

  lam1 :: β -> α -> L((β,α)) -> L((β,α))
  lam1 n m l = (:) (n,m) l

  lam2 :: L(α) -> β -> L((β,α)) -> L((β,α))
  lam2 ms n l = foldr (lam1 n) l ms

  product :: L(β) -> L(α) -> L((β,α))
  product ns ms = foldr (lam2 ms) [] ns

  where
    (:) :: α -> L(α) -> L(α)
    [] :: L(α)

Sized-type annotated program:
  foldr :: ∀ x2 x3 x4.(∀ x1.α -> L[x1]((β,γ)) -> L[x1+x2]((β,γ)))
                      -> L[x3]((β,γ))
                      -> L[x4](α) -> L[x2*x4+x3]((β,γ))
  foldr f b ((:) l ls) = f l (foldr f b ls)
  foldr f b [] = b

  lam1 :: ∀ x1.α -> β -> L[x1]((α,β)) -> L[1+x1]((α,β))
  lam1 n m l = (:) (n,m) l

  lam2 :: ∀ x1 x2.L[x1](α) -> β -> L[x2]((β,α)) -> L[x1+x2]((β,α))
  lam2 ms n l = foldr (lam1 n) l ms

  product :: ∀ x1 x2.L[x1](α) -> L[x2](β) -> L[1+x1*x2]((α,β))
  product ns ms = foldr (lam2 ms) [] ns

  where
    (:) :: ∀ x1.α -> L[x1](α) -> L[1+x1](α)
    [] :: L[1](α)

What is happening here:

  • As a first step, HoSA performs standard Hindley-Milner type inference on the supplied program.

  • HoSA then specialises the types of functions to specific call-sites, resulting in a calling-context annotated program. This enables the size annotation of polymorphic arguments. E.g., above the most general type of ‘foldr’ has been refined to

    (γ -> L((β,α)) -> L((β,α))) -> L((β,α)) -> L(γ) -> L((β,α))

    In the presence of multiple calls to the same function, HoSA collects the specialised types of each call site and computes a suitable specialisation via anti-unification. HoSA can also specialise function calls itself, e.g., when the type obtained this way is still too general. This behaviour is controlled with the flag -c <num> where <num> gives the depth of the specialisation.

  • Finally, on the resulting program sized-type inference is performed.