This is an alternative site for discovering Elm packages. You may be looking for the official Elm package site instead.

Infer.Scheme

type alias Scheme = ( List Int, Type )

A type scheme represents a variable definition, for example a named function. When the variable is used, the scheme must be converted into a concrete type. The listed type variables are the ones that the type is generic over. It may contain others that represent for example types of things defined higher up.

freeVariables : Scheme -> Set Int

Variables that are not bound by the type scheme.

type alias Environment = Dict String Scheme

Holds all names defined in outer scopes.

freshInt : Infer.Monad Int

Generates an int one greater than the last.

instantiate : Scheme -> Infer.Monad Type

Converts a scheme into a concrete type by swapping the generic type variables for fresh ones.

generalize : Environment -> Type -> Scheme

Converts a type into a type scheme that is generic over all the type variables in the type not coming from the environment.

module Infer.Scheme exposing (Scheme, freeVariables, Environment, freshInt, instantiate, generalize)

{-|


#

@docs Scheme, freeVariables
@docs Environment
@docs freshInt, instantiate, generalize

-}

import Infer.Type as Type exposing (Type, RawType(..))
import Infer.Monad as Infer
import State
import Dict exposing (Dict)
import Set exposing (Set)


{-| Generates an int one greater than the last.
-}
freshInt : Infer.Monad Int
freshInt =
    State.advance (\state -> ( Ok state, state + 1 ))


{-| A type scheme represents a variable definition, for example a named function.
When the variable is used, the scheme must be converted into a concrete type.
The listed type variables are the ones that the type is generic over. It may
contain others that represent for example types of things defined higher up.
-}
type alias Scheme =
    ( List Int, Type )


{-| Converts a scheme into a concrete type by swapping the generic type
variables for fresh ones.
-}
instantiate : Scheme -> Infer.Monad Type
instantiate ( vars, ( constraints, _ ) as t ) =
    List.map
        (\var ->
            Infer.map
                (\x ->
                    ( var
                    , ( Dict.get var constraints
                            |> Maybe.map (Dict.singleton x)
                            |> Maybe.withDefault Dict.empty
                      , TAny x
                      )
                    )
                )
                freshInt
        )
        vars
        |> Infer.combine
        |> Infer.map Dict.fromList
        |> Infer.map (\sub -> Type.substitute sub t)


{-| Holds all names defined in outer scopes.
-}
type alias Environment =
    Dict String Scheme



{- Applies a substitution on a type scheme without touching the generic type vars
   substitute : Type.Substitution -> Scheme -> Scheme
   substitute s ( vars, t ) =
   ( vars, Type.substitute (List.foldl Dict.remove s vars) t )
-}


{-| Converts a type into a type scheme that is generic over all the type variables
in the type not coming from the environment.
-}
generalize : Environment -> Type -> Scheme
generalize env t =
    let
        inEnv =
            List.map freeVariables (Dict.values env)
                |> List.foldl Set.union Set.empty

        inType =
            Type.variables <| Tuple.second t

        generic =
            Set.diff inType inEnv
    in
        ( Set.toList generic, t )


{-| Variables that are not bound by the type scheme.
-}
freeVariables : Scheme -> Set Int
freeVariables ( generic, ( _, t ) ) =
    Set.diff (Type.variables t) (Set.fromList generic)