Algebraic Datatypes
Sum types with potentially multiple different variants can be expressed by algebraic datatypes.
Defining a datatype in Effekt is similar to other languages.
type FileSystem {
File(name: String, content: String)
Directory(name: String, children: List[FileSystem])
}
A datatype definition starts with the type
keyword and is followed by the name of the datatype.
Variants are enclosed by curly braces and separated by newlines (or semicolons) and consist of a name and a (non-optional, but potentially empty) list of fields.
Constructing values of this datatype works as you might expect:
val file = File("README.md", "Effekt")
While records allow selecting fields directly, this is not true on variants of a datatype, since it might fail.
file.name // fails
Instead, pattern matching has to be used to cover all cases.
def containsFile(fs: FileSystem, name: String): Bool =
fs match {
case File(name1, _) and name1 == name => true
case Directory(_, children) =>
children.any { c => containsFile(c, name) }
} else { false }
A pattern match (initiated with the keyword match
) consists of one or more clauses delimited by case
.
There is an exhaustivity check helping you to handle all cases of the scrutinee.
Pattern guards, starting with and
, can be used to refine matches with further conditions that evaluate to a Bool
.
Optionally, like with if
expressions, a match
expression can be followed by an else
branch to provide a default case.
Instead of an else
branch in a match
expression, you may also use a wildcard pattern
(case _ => ...
).
containsFile(Directory("/", [ file ]), "README.md")
Furthermore, you can use partial matches in positions where a Bool
is expected, like here:
if (file is File(name, content)) {
println("The file " ++ name ++ " has a content length of " ++ show(content.length))
}
Lambda match
In all positions where a block is expected, like here
def withFile(name: String) { prog: FileSystem => Unit }: Unit = <>
you can directly pattern match on the single argument without needing to bind the parameter:
withFile("README.md") {
case File(_, _) => <>
case Directory(_, _) => <>
}