hayleigh-dot-dev / elm-corefn / CoreFn.Optimise.ConstFold

common : { add : Maybe CoreFn.Name.Name, sub : Maybe CoreFn.Name.Name, mul : Maybe CoreFn.Name.Name, div : Maybe CoreFn.Name.Name, neg : Maybe CoreFn.Name.Name } -> (ann -> ann -> ann) -> CoreFn.Expr.Expr ann -> CoreFn.Expr.Expr ann

A convenient helper to eliminate common maths expressions and other operators. Like all the optimisations in this module this will only eliminate expressions that only contain literals, but when combined with the constant propagation helpers defined in ConstProp you can eliminate constant variables too.

This optimisation is applied bottom-up, so it will eliminate compound expressions like 1 + 2 * 3 (assuming you provide names for your add and mul operators).

custom : List (CoreFn.Expr.Expr ann -> Maybe (CoreFn.Expr.Expr ann)) -> CoreFn.Expr.Expr ann -> CoreFn.Expr.Expr ann

Allows you to supply a list of custom eliminations that may or may not succeed. Best used in combination with the helpers defined elsewhere in this module like unop, binop, fn, and so on.

Operators

unop : CoreFn.Name.Name -> (CoreFn.Expr.Lit (CoreFn.Expr.Expr ann) -> Maybe (CoreFn.Expr.Lit (CoreFn.Expr.Expr ann))) -> CoreFn.Expr.Expr ann -> Maybe (CoreFn.Expr.Expr ann)

Eliminate a unary operator applied to some literal. We could implement an elimination for simple negation like so:

import CoreFn.Expr as Expr exposing (Expr)
import CoreFn.Name as Name
import CoreFn.Optimise.ConstFold as ConstFold

negate : Expr -> Maybe Expr
negate =
    ConstFold.unop
        (Name.Lower "negate")
        (\lit ->
            case lit of
                Expr.Int int ->
                    Just <| Expr.Int <| Basics.negate int

                Expr.Num num ->
                    Just <| Expr.Num <| Basics.negate num

                _ ->
                    Nothing
        )

binop : CoreFn.Name.Name -> (ann -> ann -> ann) -> (CoreFn.Expr.Lit (CoreFn.Expr.Expr ann) -> CoreFn.Expr.Lit (CoreFn.Expr.Expr ann) -> Maybe (CoreFn.Expr.Lit (CoreFn.Expr.Expr ann))) -> CoreFn.Expr.Expr ann -> Maybe (CoreFn.Expr.Expr ann)

Just like unop, we can eliminate a binary operator applied to two literal values with this function. You can probably guess how to implement something like addition (there are even helpers to make that easier) so let's look at a different example.

We could eliminate object/record access like so:

import CoreFn.Expr as Expr exposing (Expr)
import CoreFn.Name as Name
import CoreFn.Optimise.ConstFold as ConstFold

access : Expr -> Maybe Expr
access =
    ConstFold.binop
        (Name.Lower "access")
        (\lhs rhs ->
            case ( lhs, rhs ) of
                ( Expr.Str key, Expr.Obj obj ) ->
                    List.filter (Tuple.first >> (==) key) obj
                        |> List.head
                        |> Maybe.map Tuple.second

                _ ->
                    Nothing
        )

Such that if we're given...

{ foo = 1 }.foo

...we can eliminate the whole thing to...

1

Typed Operators

intUnop : CoreFn.Name.Name -> (Basics.Int -> Basics.Int) -> CoreFn.Expr.Expr ann -> Maybe (CoreFn.Expr.Expr ann)

numUnop : CoreFn.Name.Name -> (Basics.Float -> Basics.Float) -> CoreFn.Expr.Expr ann -> Maybe (CoreFn.Expr.Expr ann)

strUnop : CoreFn.Name.Name -> (String -> String) -> CoreFn.Expr.Expr ann -> Maybe (CoreFn.Expr.Expr ann)

intBinop : CoreFn.Name.Name -> (ann -> ann -> ann) -> (Basics.Int -> Basics.Int -> Basics.Int) -> CoreFn.Expr.Expr ann -> Maybe (CoreFn.Expr.Expr ann)

numBinop : CoreFn.Name.Name -> (ann -> ann -> ann) -> (Basics.Float -> Basics.Float -> Basics.Float) -> CoreFn.Expr.Expr ann -> Maybe (CoreFn.Expr.Expr ann)

strBinop : CoreFn.Name.Name -> (ann -> ann -> ann) -> (String -> String -> String) -> CoreFn.Expr.Expr ann -> Maybe (CoreFn.Expr.Expr ann)

Functions

The following collection of functions reduce some of the boilerplate when optimising for n-arity functions. Because the CoreFn expression models function application as repeaded application of single-argument functions, there ends up being quite a bit of nesting if you want to pull out 3, 4, ..8 arguments!

f : CoreFn.Name.Name -> (CoreFn.Expr.Expr ann -> Maybe (CoreFn.Expr.Expr ann)) -> CoreFn.Expr.Expr ann -> Maybe (CoreFn.Expr.Expr ann)

f2 : CoreFn.Name.Name -> (CoreFn.Expr.Expr ann -> CoreFn.Expr.Expr ann -> Maybe (CoreFn.Expr.Expr ann)) -> CoreFn.Expr.Expr ann -> Maybe (CoreFn.Expr.Expr ann)

f3 : CoreFn.Name.Name -> (CoreFn.Expr.Expr ann -> CoreFn.Expr.Expr ann -> CoreFn.Expr.Expr ann -> Maybe (CoreFn.Expr.Expr ann)) -> CoreFn.Expr.Expr ann -> Maybe (CoreFn.Expr.Expr ann)

f4 : CoreFn.Name.Name -> (CoreFn.Expr.Expr ann -> CoreFn.Expr.Expr ann -> CoreFn.Expr.Expr ann -> CoreFn.Expr.Expr ann -> Maybe (CoreFn.Expr.Expr ann)) -> CoreFn.Expr.Expr ann -> Maybe (CoreFn.Expr.Expr ann)

f5 : CoreFn.Name.Name -> (CoreFn.Expr.Expr ann -> CoreFn.Expr.Expr ann -> CoreFn.Expr.Expr ann -> CoreFn.Expr.Expr ann -> CoreFn.Expr.Expr ann -> Maybe (CoreFn.Expr.Expr ann)) -> CoreFn.Expr.Expr ann -> Maybe (CoreFn.Expr.Expr ann)

f6 : CoreFn.Name.Name -> (CoreFn.Expr.Expr ann -> CoreFn.Expr.Expr ann -> CoreFn.Expr.Expr ann -> CoreFn.Expr.Expr ann -> CoreFn.Expr.Expr ann -> CoreFn.Expr.Expr ann -> Maybe (CoreFn.Expr.Expr ann)) -> CoreFn.Expr.Expr ann -> Maybe (CoreFn.Expr.Expr ann)

f7 : CoreFn.Name.Name -> (CoreFn.Expr.Expr ann -> CoreFn.Expr.Expr ann -> CoreFn.Expr.Expr ann -> CoreFn.Expr.Expr ann -> CoreFn.Expr.Expr ann -> CoreFn.Expr.Expr ann -> CoreFn.Expr.Expr ann -> Maybe (CoreFn.Expr.Expr ann)) -> CoreFn.Expr.Expr ann -> Maybe (CoreFn.Expr.Expr ann)

f8 : CoreFn.Name.Name -> (CoreFn.Expr.Expr ann -> CoreFn.Expr.Expr ann -> CoreFn.Expr.Expr ann -> CoreFn.Expr.Expr ann -> CoreFn.Expr.Expr ann -> CoreFn.Expr.Expr ann -> CoreFn.Expr.Expr ann -> CoreFn.Expr.Expr ann -> Maybe (CoreFn.Expr.Expr ann)) -> CoreFn.Expr.Expr ann -> Maybe (CoreFn.Expr.Expr ann)