Functions for working with Elm source code.
The compiler phases in general look like this:
Useful eg. for tools like elm-format
that convert back to the Elm
representation, trying to do as few changes as possible. Ie. it would be bad
if elm-format
changed
\a b c -> a + b + c
to
\a -> \b -> \c -> a + b + c
That transformation is one of the things the Desugar phase does. So tools like
elm-format
probably don't want to touch that phase, and will only want to parse!
parseExpr : Elm.Data.FileContents.FileContents -> Result Error Elm.AST.Frontend.LocatedExpr
Parse a single expression like
( 12, "Hello" )
into AST like
Located
{start = ..., end = ...}
(Tuple
(Located ... (Int 12))
(Located ... (String "Hello"))
)
If you don't need the location information and want to only keep the expressions,
use Elm.AST.Frontend.unwrap
to get something like
Tuple
(Int 12)
(String "Hello")
parseModule : { filePath : Elm.Data.FilePath.FilePath, sourceCode : Elm.Data.FileContents.FileContents } -> Result Error (Elm.Data.Module.Module Elm.AST.Frontend.LocatedExpr)
Parse a module (one *.elm
file). Get a Module
datastructure back, holding
the information about its exposed values, imports, declarations and more.
A file like
module Main exposing (foo)
import Bar as B exposing (bar)
foo =
123
will get parsed into
{ imports =
Dict.fromList
[ ( "Bar"
, { moduleName = "Bar"
, as_ = Just "B"
, exposing_ = Just (ExposingSome [ ExposedValue "bar" ])
}
)
]
, name = "Foo"
, filePath = "src/Foo.elm" -- what you pass into the function
, declarations =
Dict.fromList
[ ( "foo"
, { module_ = "Foo"
, name = "foo"
, body = Value (AST.Frontend.Int 123)
}
)
]
, type_ = PlainModule
, exposing_ = ExposingSome [ ExposedValue "foo" ]
}
parseModules : List { filePath : Elm.Data.FilePath.FilePath, sourceCode : Elm.Data.FileContents.FileContents } -> Result Error (Dict Elm.Data.ModuleName.ModuleName (Elm.Data.Module.Module Elm.AST.Frontend.LocatedExpr))
Parse multiple modules (*.elm
files) - see parseModule
for details.
parseImport : Elm.Data.FileContents.FileContents -> Result Error Elm.Data.Import.Import
Parse a single import statement, like
import Foo as F exposing
( foo
, Bar(..)
, Baz
)
into
{ moduleName = "Foo"
, as_ = Just "F"
, exposing_ =
Just
(ExposingSome
[ ExposedValue "foo"
, ExposedTypeAndAllConstructors "Bar"
, ExposedType "Baz"
]
)
}
parseDeclaration : { moduleName : Elm.Data.ModuleName.ModuleName, declaration : Elm.Data.FileContents.FileContents } -> Result Error (Elm.Data.Declaration.Declaration Elm.AST.Frontend.LocatedExpr)
Parse a single declaration, like
foo =
123
into
{ module_ = "Foo" -- what you pass into the function
, name = "foo"
, body = Value (AST.Frontend.Int 123)
}
After we parse the source code from a String
to the AST, we desugar it -
simplify the AST type as much as possible to make later phases simpler and easier.
The best example to illustrate this (it doesn't actually happen though!) is
let
vs where
. Imagine if Elm allowed for where
constructs in its syntax,
like Haskell does:
foo = x + y
where
x = 123
y = x + 2
Then we'd like to convert these to let
constructs (or the other way round)
as soon as possible, so that the other phases don't need to handle two
almost identical scenarios all over the place.
Examples of real desugarings include:
desugarExpr : Dict Elm.Data.ModuleName.ModuleName (Elm.Data.Module.Module Elm.AST.Frontend.LocatedExpr) -> Elm.Data.Module.Module Elm.AST.Frontend.LocatedExpr -> Elm.AST.Frontend.LocatedExpr -> Result Error Elm.AST.Canonical.LocatedExpr
Desugar a single expression like
Lambda
{ arguments = [ "x", "y" ]
, body = AST.Frontend.Int 42
}
into AST like
Lambda
{ argument = "x"
, body =
Lambda
{ argument = "y"
, body = AST.Frontend.Int 42
}
}
desugarModule : Dict Elm.Data.ModuleName.ModuleName (Elm.Data.Module.Module Elm.AST.Frontend.LocatedExpr) -> Elm.Data.Module.Module Elm.AST.Frontend.LocatedExpr -> Result Error (Elm.Data.Module.Module Elm.AST.Canonical.LocatedExpr)
Desugar a module (one *.elm
file).
desugarModules : Dict Elm.Data.ModuleName.ModuleName (Elm.Data.Module.Module Elm.AST.Frontend.LocatedExpr) -> Result Error (Dict Elm.Data.ModuleName.ModuleName (Elm.Data.Module.Module Elm.AST.Canonical.LocatedExpr))
Desugar multiple modules (*.elm
files) - see desugarModule
for details.
desugarOnlyModule : Elm.Data.Module.Module Elm.AST.Frontend.LocatedExpr -> Result Error (Elm.Data.Module.Module Elm.AST.Canonical.LocatedExpr)
Desugar a module (one *.elm
file), without the intention of desugaring
another one.
These functions compute the types of the given expressions, as well as check them against the user-defined type annotations.
Note that the more of your code you'll give these functions at once, the better
the type inference will be. So it's advisable to eg. run inferModules
once instead of running inferModule
on each of your modules.
inferExpr : Elm.AST.Canonical.LocatedExpr -> Result Error Elm.AST.Typed.LocatedExpr
Infer the types of a single expression.
inferModule : Elm.Data.Module.Module Elm.AST.Canonical.LocatedExpr -> Result Error (Elm.Data.Module.Module Elm.AST.Typed.LocatedExpr)
Infer the types of expressions in a module (a single *.elm
file).
inferModules : Dict Elm.Data.ModuleName.ModuleName (Elm.Data.Module.Module Elm.AST.Canonical.LocatedExpr) -> Result Error (Dict Elm.Data.ModuleName.ModuleName (Elm.Data.Module.Module Elm.AST.Typed.LocatedExpr))
Infer the types of expressions in multiple modules (*.elm
files).
After typechecking the expressions are ready to be optimized. (The inferred types are available to you inside the optimizations! (Unfortunately, the location information is also available to you inside the optimization. Sorry.))
defaultOptimizations : List ( String, Elm.AST.Typed.LocatedExpr -> Maybe Elm.AST.Typed.LocatedExpr )
The default optimizations the elm-in-elm compiler uses.
Try evaluating it in the REPL: you should see that each optimization function
has a name String next to it.
> import Elm.Compiler
> Elm.Compiler.optimizations
[ ("plus", ...)
, ("cons", ...)
, ("if-literal-bool", ...)
]
>
You can use this to filter optimizations you don't want!
wantedOptimizations = Set.fromList [ "plus", "if-literal-bool ]
optimizations
|> List.filter (\(name, _) -> Set.member name wantedOptimizations)
optimizeExpr : Elm.AST.Typed.LocatedExpr -> Elm.AST.Typed.LocatedExpr
Optimize a given (typed) expression using the default set of optimizations.
For using your own optimizations instead of or in addition to the default ones,
look at the optimizeExprWith
function.
optimizeExprWith : List ( String, Elm.AST.Typed.LocatedExpr -> Maybe Elm.AST.Typed.LocatedExpr ) -> Elm.AST.Typed.LocatedExpr -> Elm.AST.Typed.LocatedExpr
Optimize a given (typed) expression using a custom set of optimizations.
optimizeModule : Elm.Data.Module.Module Elm.AST.Typed.LocatedExpr -> Elm.Data.Module.Module Elm.AST.Typed.LocatedExpr
Optimize all expressions in a given module using the default set of optimizations.
Note there is currently no inter-definition optimizations (inlining etc.) - only the optimizations on each separate expression.
For using your own optimizations instead of or in addition to the default ones,
look at the optimizeModuleWith
function.
optimizeModuleWith : List ( String, Elm.AST.Typed.LocatedExpr -> Maybe Elm.AST.Typed.LocatedExpr ) -> Elm.Data.Module.Module Elm.AST.Typed.LocatedExpr -> Elm.Data.Module.Module Elm.AST.Typed.LocatedExpr
Optimize all expressions in a given module using a custom set of optimizations.
Note there is currently no inter-definition optimizations (inlining etc.) - only the optimizations on each separate expression.
optimizeModules : Dict Elm.Data.ModuleName.ModuleName (Elm.Data.Module.Module Elm.AST.Typed.LocatedExpr) -> Dict Elm.Data.ModuleName.ModuleName (Elm.Data.Module.Module Elm.AST.Typed.LocatedExpr)
Optimize all expressions in multiple modules using the default set of optimizations.
For using your own optimizations instead of or in addition to the default ones,
look at the optimizeModulesWith
function.
optimizeModulesWith : List ( String, Elm.AST.Typed.LocatedExpr -> Maybe Elm.AST.Typed.LocatedExpr ) -> Dict Elm.Data.ModuleName.ModuleName (Elm.Data.Module.Module Elm.AST.Typed.LocatedExpr) -> Dict Elm.Data.ModuleName.ModuleName (Elm.Data.Module.Module Elm.AST.Typed.LocatedExpr)
Optimize all expressions in multiple modules using a custom set of optimizations.
If you want to typecheck the code but then don't do anything with the types afterwards, you can drop them from the expressions you have. This is essentially a move backwards in the compiler phases:
dropTypesExpr : Elm.AST.Typed.LocatedExpr -> Elm.AST.Canonical.LocatedExpr
Drop types from a single expression.
We're hitting limitations of the Elm Packages website, and the type shown isn't very descriptive. The real type of this function is:
Typed.LocatedExpr -> Canonical.LocatedExpr
Example usage:
Located
{ start = ..., end = ... }
( Tuple
(Located ... ( Int 12, Type.Int ))
(Located ... ( String "Hello", Type.String ))
, Type.Tuple Type.Int Type.String
)
becomes
Located
{ start = ..., end = ... }
(Tuple
(Located ... (Int 12)
(Located ... (String "Hello")
)
If location info is not useful to you either, look for the unwrap
functions
in the various Elm.AST.*
modules.
dropTypesModule : Elm.Data.Module.Module Elm.AST.Typed.LocatedExpr -> Elm.Data.Module.Module Elm.AST.Canonical.LocatedExpr
Drop types from all expressions in the module.
We're hitting limitations of the Elm Packages website, and the type shown isn't very descriptive. The real type of this function is:
Module Typed.LocatedExpr
-> Module Canonical.LocatedExpr
dropTypesModules : Dict Elm.Data.ModuleName.ModuleName (Elm.Data.Module.Module Elm.AST.Typed.LocatedExpr) -> Dict Elm.Data.ModuleName.ModuleName (Elm.Data.Module.Module Elm.AST.Canonical.LocatedExpr)
Drop types from all expressions in all the modules.
We're hitting limitations of the Elm Packages website, and the type shown isn't very descriptive. The real type of this function is:
Dict ModuleName (Module Typed.LocatedExpr)
-> Dict ModuleName (Module Canonical.LocatedExpr)