NAME
    Functional::Types - a Haskell-inspired type system for Perl

SYNOPSIS
      use Functional::Types;

      sub ExampleType { newtype }
      sub MkExampleType { typename ExampleType, Record(Int,String), @_ }

      type my $v = ExampleType;
      bind $v, MkExampleType(42,"forty-two");
      say show $v;
      my $uv = untype $v;

DESCRIPTION
    Functional::Types provides a runtime type system for Perl, the main
    purpose is to allow type checking and have self-documenting data
    structures. It is strongly influenced by Haskell's type system. More
    details are below, but at the moment they are not up-to-date. The /t
    folder contains examples of the use of each type.

AUTHOR
    Wim Vanderbauwhede <Wim.Vanderbauwhede@mail.be>

COPYRIGHT
    Copyright 2015- Wim Vanderbauwhede

LICENSE
    This library is free software; you can redistribute it and/or modify it
    under the same terms as Perl itself.

SEE ALSO
NEWTYPE
    The function of newtype is to glue typename information together with
    the constructor information, and typecheck the arguments to the
    constructor. I think it is best to treat all cases separately:

      - Primitive types: e.g. sub ArgType { newtype String,@_ } # Does ArgType expect a String or a bare value? I guess a bare value is better?
      - Record types: e.g. sub MkVarDecl { newtype VarDecl, Record( acc1 => ArgType, acc2 => Int), @_ }
      - Variant types: e.g. sub Just { newtype Maybe(a), Variant(a), @_ }
      - Map type: sub HashTable { newtype Map(String,Int), @_ } is a primitive type
      - Array type: sub IntList { newtype Array(Int), @_ } is a primitive type

    I expect String to be called and it will return ['$',String] so
    ArgType(String("str")) should typecheck

      String("str") will return {Type => ['$',String], Val =>"str"}

      MkVarDecl will return {Type => ['~',MkVarDecl,[],VarDecl,[]], Val => ...}
      Just(Int(42)) will return {Type => ['|',Just,[{a => 'any'}],Maybe,[{a => 'any'}}, Val => {Type => ['$',Int], Val => 42}}

    To typecheck this against type Maybe(Int) will require checking the type
    of the Val So maybe newtype must do this: if the typename or type ctor
    (yes, rather) has a variable then we need the actual type of the value

TYPECHECKING
    This is a bit rough. We should maybe just check individual types, and
    always we must use the constructed type as a starting point, and the
    declared type to check against. We typecheck in two different contexts:

      1/ Inside the newtype() call: for every element in @_, we should check against the arguments of the type constructor. 
      2/ Inside the bind() call: this call takes a typed value. For this typed value, all we really need to check is if its typename matches with the declared name.

    I think it might be better to have the same Type record structure for
    every type:

      Variant, Record: ['|~:', $ctor, [@ctor_args],$typename,[@typename_args]]

      Map, Tuple, Array: ['@%*', $ctor, [@ctor_args], $typename=$ctor,[]]

      Scalar: ['$', $ctor, [@ctor_args], $typename=$ctor,[]]

BIND
      bind():
  
      bind $scalar, Int($v);
      bind $list, SomeList($vs);
      bind $map, SomeMap($kvs);
      bind $rec, SomeRec(...); 
      bind $func, SomeFunc(...);

    For functions, bind() should do:

      - Take the arguments, which should be typed, typecheck them;
      - call the original function with the typed args
      - the return value should also be typed, just return it.

    So it might be very practical to have a typecheck() function

    Furthermore, we can do something similar to pattern matching by using a
    variant() function like this:

      given(variant $t) {
            when (Just) : untype $t;
            when (Nothing) : <do something else>
      }

    So variant() simply extracts the type constructor from a Variant type.

PROTOTYPES
    * These are *not* to be called directly, only as part of a newtype call,
    unless you know what you're doing.

    * I realise it would be faster for sure to have numeric codes rather
    than strings for the different prototypes.

    The prototype call returns information on the kind of type, the type
    constructor and the arguments. Currently:

    * PRIM, storing untyped values:

      Scalar: ['$', $type], Val = $x => NEVER used as-is
  
      Array: ['@', $type], Val = [@xs] 
      Hash: ['%', [$ktype,$vtype]], Val = {@kvpairs}
      Tuple: ['*', [@tupletypes]], Val = [@ts]

    * PROPER, storing only typed values:

      Variant: ['|', $ctor, [@ctor_args],$typename,[@typename_args]], Val = ???
      Record: ['~', $ctor, [@ctor_args],$typename,[@typename_args]], Val = ???
      Record with fields: [':', $ctor, [@ctor_args_fields],$typename,[@typename_args]] , Val = {}

    * FUNCTION, the function can itself take typed values or untyped ones,
    depending on cast() or bind() What we store is actually a wrapper around
    the function, to deal with the types So we should somehow get the
    original function back. I think we can do this by calling the wrapper
    without any arguments, in which case it should return a typed value with
    the function's type in Type and the original function in Value Anyhow
    untype() only makes sense for a function that works on untyped values of
    course

      Function: ['&',[@function_arg_types]], Val = \&f

    In a call to type() the argument will only return
    [$typename,[@typename_args]] For a scalar type I could just return
    $typename but maybe consistency?

    In a newtype call, the primitive types don't have a constructor. There
    is some asymmetry in the '$' type compared to the others:

    Normally the pattern is Prototype($typename) but for primitive types it
    is just Scalar() and the prim type's typename comes from caller()

    Also, prim types are created without newtype(), I think I should hide
    this behaviour.

    Maybe I need to distinguish between a new data and a type alias, it
    would certainly clarify things; Also, I guess for a type alias for a
    prim type we can feed it an untyped value.