Keywords
Summary#
| Keyword | Used for |
|---|---|
module, exposing | declaring a module and what it makes public |
import, as, exposing | using other modules |
type | declaring a custom type |
type alias | naming a type |
if, then, else | conditional expressions |
when, is | pattern matching |
let, in | local definitions |
as (in patterns) | naming a whole value while destructuring it |
class, instance, where | declaring classes and instances (first-party packages only, for now) |
where (in a type) | a refinement: (n : Int where n > 0) |
moved (in a type) | an argument the function uses up: (moved b : Builder a) |
Attributes start with @ and come before a declaration:
| Attribute | Used for |
|---|---|
@derive(...) | asking for Eq, Ord or Inspect on an abstract type |
@extern(...) | declaring a function implemented in the host language |
@externPure(...) | the same, for a function with no effects |
@capability | marking a value only the application may use |
@affine | marking a custom type that holds a builder, so it is used once |
Modules#
module names the file’s module and lists what it exposes:
module Inventory exposing (Sku, Item, restock, summary)
To expose a custom type together with its constructors, write Type(..).
Without (..), the type is abstract: other modules can use it, but cannot
build or match on its values directly.
module Shape exposing (Shape(..), area)
import brings in another module. as gives it a shorter name, and
exposing lets you use some of its names unqualified:
import Dict exposing (Dict)
import Beam.Server as Server
import Task exposing (Task)
Geng forbids exposing (..) on a whole module, in both module and import
lines, so every name used unqualified is listed where it is imported. (This
rule is decided but the compiler does not enforce it yet.)
Types#
type declares a custom type. Each constructor takes at most one argument,
which is often a record:
type Shape
= Circle { radius : Float }
| Rectangle { width : Float, height : Float }
| Point
type alias gives an existing type a name. It does not create a new type:
an Item below is interchangeable with any record of the same shape.
type alias Item =
{ name : String
, priceCents : Int64
}Conditionals#
if is an expression, and every if has an else:
sign : Int -> String
sign n =
if n < 0 then
"negative"
else if n == 0 then
"zero"
else
"positive"Pattern matching#
when ... is matches a value against patterns. The compiler checks that every
possible value is covered.
area : Shape -> Float
area shape =
when shape is
Circle { radius } ->
Math.pi * radius * radius
Rectangle { width, height } ->
width * height
Point ->
0.0
Patterns can be literals, constructors, records, arrays and _, which matches
anything:
describe : Array String -> String
describe args =
when args is
[] ->
"no arguments"
[ one ] ->
"one argument: " ++ one
_ ->
"several arguments"
as names a whole value while also destructuring it:
when shape is
(Rectangle { width }) as rect ->
...
Elm uses case ... of. Gren and Geng use when ... is.
Local definitions#
let ... in defines names that are visible only in the expression after in.
Definitions can have type annotations:
summary : Array Item -> String
summary items =
let
cents : Int64
cents =
total (Array.map .priceCents items)
in
"value " ++ inspect cents
A name cannot shadow another name that is already in scope.
Classes and instances#
class declares a set of operations, and instance implements them for a
type. A constraint arrow => says that a function needs an instance:
class Eq a where
eq : a -> a -> Bool
member : Eq a => a -> Array a -> Bool
For now, only Geng’s own first-party packages can declare classes and
instances; opening both to everyone is planned. Application code uses the
classes core provides: Eq, Ord, Inspect, Num, Integral,
Fractional, Bits and Appendable. class and instance are contextual
keywords, so they can still be used as ordinary names elsewhere.
Attributes#
@derive asks the compiler for structural Eq, Ord or Inspect on an
abstract type. A type whose constructors are exposed gets them automatically.
@derive(Eq, Ord, Inspect)
type Sku
= Sku { vendor : String, code : UInt32 }
@extern declares a function whose implementation is in the host language,
with one line per language. @externPure is the same for a function with no
effects. See Interop.
@extern(js, "Time", "now")
nowWith : (Int64 -> posix) -> Task x posix
@capability marks a value, usually a permission, that only the application
may use. A dependency that tries to use it gets a compile error, so a library
can only touch the file system or the network if the application hands it a
permission.
@affine marks a custom type that holds an Array.Builder, so its values
are used once. A record that holds one
needs no mark.
@affine
type Document
= Document { title : String, body : Builder Line }
moved is not an attribute but a word before an argument’s name in a
signature, (moved b : Builder a). It is only special there, so moved can
still be used as an ordinary name.
Words that are no longer keywords#
caseandofare Elm’s; Geng useswhenandis.portis an ordinary name.port moduleandeffect moduleare refused, with a message pointing to@externandSource.infixis not available outsidecore. See Operators.
New keywords are only added in major versions, with a tool to migrate code that used the word as a name.