Overview

Elm Friday: Type Annotations (Part VI)

No Comments

One of Elm’s most important characteristics is its static type system. This enables Elm to make much stronger guarantees during run time compared to dynamic languages like JavaScript. This boils down to “If it compiles, it’ll never throw a runtime exception”. In this episode, we’ll look into the type system and on type annotations in particular more closely.

About This Series

This is the sixth post in a series of short and sweet blog posts about Elm. The stated goal of this series is to take you from “completely clueless about Elm” to “chief Elm guru”, step by step. If you have missed the previous episodes, you might want to check out the table of contents.

Type Annotations Versus Type Inference

Although Elm is a statically typed language, our examples in the previous posts had no type declarations whatsoever. The reason is that Elm can infer the type of almost any expression. That means that everything is typed, either explicitly by adding a type annotation or implicitly by relying on Elm’s type inference.

Let’s revisit some of the functions from the last episode and add type annotations to them. The type annotations are the line just before the function definition:

import Html

multiply : number -> number -> number
multiply a b = a * b

square : number -> number
square a = multiply a a

productOfSquares : number -> number -> number
productOfSquares a b = multiply (square a) (square b)

incrementAll : List number -> List number
incrementAll list = List.map (\ n -> n + 1) list

incrementAll2 : List number -> List number
incrementAll2 = List.map (\ n -> n + 1)

main : Html.Html
main =
  let
    print n = Html.text <| toString n
  in
    Html.p []
    [ print <| multiply 3 5
    , Html.br [] []
    , print <| square 4
    , Html.br [] []
    , print <| productOfSquares 2 3
    , Html.br [] []
    , print <| incrementAll [1, 2, 3]
    , Html.br [] []
    , print <| incrementAll2 [1, 2, 3]
    ]

(Remark: I refactored the main function a bit from the last episode by extracting the duplicated conversion from number/list into an HTML text element.)

To pick one example, square : number -> number is the type annotation for the function definition of square. A type annotation should be written directly in the line above the function definition. It is comprised of the function name, a colon, and the types of all input parameters and the return type, each separated by ->.

There is no distinction between a parameter type and the return type. Coming from other languages, you might expect that the type annotation for multiply somehow makes it clear that two numbers go in and one number comes out. For example the type signature could read (number, number) -> number. It doesn't. The separator between the first and second input parameter is ->, just as the separator between the second input parameter and the return type. This is because talking about first and second input parameter is just one way of thinking about multiply. You could also say that multiply takes only one parameter and returns a function with the signature number -> number. Both are equally valid points of view. You can either think of this function as (number, number) -> number or number -> (number -> number). The reason is that Elm supports currying and partial function application naturally.

Here is a code example that illustrates this:

 import Html

multiply : number -> number -> number
multiply a b = a * b

multiplyByFive : number -> number
multiplyByFive = multiply 5

-- The expression (multiply 5) yields a new function with signature
-- (number -> number) by partial appication, that is: the first argument to
-- multiply is provided, but not the second.

main : Html.Html
main = multiplyByFive 3 |> toString |> Html.text

  • Page
  • 1
  • 2

Comment

Your email address will not be published. Required fields are marked *