A quick reference comparison for syntax and features between languages. Github
Comments
-- This is a comment
-- This is a comment
# This is a comment
-- This is a comment
Primitives
1 : Int
1.1 : Float
True : Bool
False : Bool
'c' : Char
"hello" : String
-- multi-line String
"""
This is useful for holding JSON or other
content that has "quotation marks".
"""
(1,'c') : (Int, Char)
Just 123 : Maybe Int
Nothing : Maybe a
Ok 123 : Result err Int
Err "Problem!" : Result String a
[ 1, 2, 3 ] : List Int
Array.fromList [ 1, 2, 3] : Array Int
Set.fromList [1, 2, 3] : Set Int
x : Dict Int Bool
x = Dict.fromList [ ( 1 , True ), ( 2, False ) ]
-- Unit type
() : ()
1 :: Int
1.1 :: Float
True :: Bool
False :: Bool
'c' :: Char
"hello" :: String
-- Requires raw-strings-qq package
{-# LANGUAGE QuasiQuotes #-}
import Text.RawString.QQ
[r|
This is useful for holding JSON or other
content that has "quotation marks".
|]
(1,'c') :: (Int, Char)
Just 123 :: Maybe Int
Nothing :: Maybe a
Right 123 :: Either err Int
Left "Problem!" :: Either String a
[1,2,3] :: [Int] -- Note: type List a = [a]
import qualified Data.Vector as Vector
Vector.fromList [1,2,3] :: Vector Int
import qualified Data.Set as Set
Set.fromList [1,2,3] : Set Int
import qualified Data.Map as Map
x :: Map Int Bool
x = Map.fromList [(1,True), (2, False)]
-- Unit type
() :: ()
1 : I32
1.1 : F64
True : Bool
False : Bool
'c' : U8
"hello" : Str
# multi-line String
"""
This is useful for holding JSON or other
content that has "quotation marks".
"""
# Roc does not have tuple syntax
# or a built-in Maybe type
Ok 123 : Result I32 *
Err "Problem!" : Result * Str
[ 1, 2, 3 ] : List I32
# Roc does not have an Array type,
# but List is implemented using
# a flat array instead of a linked list
[: 1, 2, 3 :] : Set Int
x : Dict I32 Bool
x = {: 1 => True, 2 => False :}
-- Unit type
{} : {}
1: Int
1.1: Float
True: Bool
False: Bool
-- no char literal
"hello": String
-- multi-line String
"
This is useful(?) for holding JSON or other
content that has \"quotation marks\".
"
#(1,"c"): #(Int, String)
Some(123): Option(Int)
None: Option(a)
Ok(123): Result(Int, err)
Error("Problem!"): Result(String, a)
[1, 2, 3]: List(Int)
-- no Array in stdlib, JS thing see gleam_javascript
set.from_list([1, 2, 3]): Set(Int)
let x: Map(Int, Bool) = map.from_list([#(1 , True), #(2, False )])
-- Unit type
Nil: Nil
Custom types
Note: Custom types are sometimes called tagged unions. Some communities call them ADTs.
type Status = Todo | Doing | Done
Todo : Status
Doing : Status
Done : Status
data Status = Todo | Doing | Done
Todo :: Status
Doing :: Status
Done :: Status
Status : [ Todo, Doing, Done ]
Todo : Status
Doing : Status
Done : Status
type Status { Todo Doing Done }
Todo: Status
Doing: Status
Done: Status
Custom types can be polymorphic. Maybe a is an example:
type Maybe a = Just a | Nothing
Just 123 : Maybe Int
Nothing : Maybe a
data Maybe a = Just a | Nothing
Just 123 :: Maybe Int
Nothing :: Maybe a
Maybe a : [ Just a, Nothing ]
Just 123 : Maybe Int
Nothing : Maybe *
type Maybe(a) { Just(a) Nothing }
Just(123): Maybe(Int)
Nothing: Maybe(a)
Records
point = -- create a record
{ x = 3, y = 4 }
point.x -- access field
List.map .x [ point, { x = 0, y = 0 } ] -- field access function
{ point | x = 6 } -- update a field
{ point -- update many fields
| x = point.x + 1
, y = point.y + 1
}
dist { x, y } = -- pattern matching on fields
sqrt (x ^ 2 + y ^ 2)
type alias Location = -- type aliases for records
{ line : Int
, column : Int
}
-- Haskell has no anonymous records
x point
fmap x [point,point]
point { x = 6 }
point
{ x = (x point) + 1
, y = (y point) + 1
}
dist Point{..} = -- Requires RecordWildCards
sqrt (x^2 + y^2)
data Location =
Location
{ line : Int
, column : Int
}
point =
{ x: 3, y: 4 }
point.x
List.map [ point, { x: 0, y: 0 } ] .x
{ point & x: 6 }
{
point &
x: point.x + 1,
y: point.y + 1,
}
dist = \{ x, y } ->
sqrt (x ^ 2 + y ^ 2)
Location :
{
line : I32,
column : I32,
}
-- no anonymous records
type Point { -- nominal types for records
Point(x: Int, y: Int)
}
const point = Point(3, 4) -- create a record
point.x -- access field
list.map([ point, Point(x: 0, y: 0) ], fn(p) { p.x }) -- field access function
Point(..point, x: 6) -- update a field
Point(..point, -- update many fields
x: point.x + 1,
y: point.y + 1,
)
import gleam/int
fn dist(point) { -- no pattern matching on fields in function heads
let Point(x,y) = point
int.square_root(int.power(x, 2) + int.power(y, 2))
}
type Location { -- nominal types for records
Location(line: Int, column: Int)
}
Functions
addOne : Int -> Int
addOne x = x + 1
add : Int -> Int -> Int
add x y = x + y
add : Int -> Int -> Int
add = (+)
poly : a -> Int
poly x = 14
addOne :: Int -> Int
addOne x = x + 1
add :: Int -> Int -> Int
add x y = x + y
add :: Int -> Int -> Int
add = (+)
poly :: a -> Int
poly x = 14
addOne : I32 -> I32
addOne = \x -> x + 1
add : I32, I32 -> I32
add = \x, y -> x + y
add : I32, I32 -> I32
add = Num.add
# Roc does not have (+) syntax for operators;
# instead, operators desugar to
# plain function calls like Num.add
poly : * -> I32
poly = \x -> 14
addOne(x: Int) = x + 1
add(x: Int, y: Int) = x + y
add : Int -> Int -> Int
add = (+)
Anonymous Functions
square =
\n -> n ^ 2
squares =
List.map (\n -> n ^ 2) (List.range 1 100)
square =
\n -> n ^ 2
squares =
fmap (\n -> n ^ 2) [1..100]
square = \n ->
n ^ 2
squares =
List.map (List.range 1 100) \n -> n ^ 2
Function application & composition
All the following are equivalent.
viewNames names =
String.join ", " (List.sort names)
viewNames names =
String.join ", " <| List.sort names
viewNames names =
names
|> List.sort
|> String.join ", "
viewNames =
String.join ", " << List.sort
viewNames =
List.sort >> String.join ", "
import qualified Data.List as List
import Data.Function ((&))
import Control.Arrow ((>>>))
viewNames names =
List.intercalate ", " (List.sort names)
viewNames names =
List.intercalate ", " $ List.sort names
viewNames names =
names
& List.sort
& (List.intercalate ", ")
viewNames =
List.intercalate ", " . List.sort
viewNames =
List.sort . List.intercalate ", "
viewNames = \names ->
Str.join (List.sort names Str.alphabetical) ", "
# Roc does not have an equivalent of
# the `<|` operator.
viewNames = \names ->
names
|> List.sort Str.alphabetical
|> Str.join ", "
# Roc does not have an equivalent of
# the `<<` operator
# or the `>>` operator.
Conditionals
if power > 9000 then "Wow!" else "Meh"
if key == 40 then
n + 1
else if key == 38 then
n - 1
else
n
if power > 9000 then "Wow!" else "Meh"
if key == 40 then
n + 1
else if key == 38 then
n - 1
else
n
if power > 9000 then "Wow!" else "Meh"
if key == 40 then
n + 1
else if key == 38 then
n - 1
else
n
Pattern matching
case maybe of
Just xs -> xs
Nothing -> []
case xs of
hd::tl -> Just (hd,tl)
[] -> Nothing
fib n =
case n of
0 -> 1
1 -> 1
_ -> fib (n-1) + fib (n-2)
case maybe of
Just xs -> xs
Nothing -> []
case xs of
hd:tl -> Just (hd,tl)
[] -> Nothing
fib n =
case n of
0 -> 1
1 -> 1
_ -> fib (n-1) + fib (n-2)
when maybe is
Just xs -> xs
Nothing -> []
# Roc does not have special
# pattern matching syntax for lists
fib = \n ->
when n is
0 -> 1
1 -> 1
_ -> fib (n - 1) + fib (n - 2)
Let expressions
functionWithLetDecs =
let
( three, four ) =
( 3, 4 )
hypotenuse a b =
sqrt (a^2 + b^2)
in
hypotenuse three four
functionWithLetDecs =
let
( three, four ) =
( 3, 4 )
hypotenuse a b =
sqrt (a^2 + b^2)
in
hypotenuse three four
functionWithLetDecs =
Pair three four =
Pair 3 4
hypotenuse = \a, b ->
Num.sqrt (a ^ 2 + b ^ 2)
hypotenuse three four
# No let/in required, indentation sensitive
Modules
module MyModule exposing (..)
-- qualified imports
import List -- List.map, List.foldl
import List as L -- L.map, L.foldl
-- open imports
import List exposing (..) -- map, foldl, concat, ...
import List exposing ( map, foldl ) -- map, foldl
import Maybe exposing ( Maybe ) -- Maybe
import Maybe exposing ( Maybe(..) ) -- Maybe, Just, Nothing
module MyModule where
-- qualified imports
import qualified List -- List.map, List.foldl
import qualified List as L -- L.map, L.foldl
-- open imports
import List -- map, foldl, concat, ...
import List ( map, foldl ) -- map, foldl
import Maybe ( Maybe ) -- Maybe
import Maybe ( Maybe(..) ) -- Maybe, Just, Nothing
interface MyModule
# Roc has no "expose everything" syntax
exposes [ foo, blah ]
imports
[
# Qualified imports
List as L, # L.map, L.walk
# Open imports (Roc has no "expose everything" syntax)
List.{ map, walk },
# Imports the Result module and also the Result type
Result.{ Result },
]
Type Annotations
answer : Int
answer =
42
factorial : Int -> Int
factorial n =
List.product (List.range 1 n)
distance : { x : Float, y : Float } -> Float
distance {x,y} =
sqrt (x^2 + y^2)
answer :: Int
answer =
42
factorial :: Int -> Int
factorial n =
List.product (List.range 1 n)
-- Haskell has no anonymous records
data Point = Point { x :: Float, y :: Float }
distance :: Point -> Float
distance Point{..} = -- Requires RecordWildCards
sqrt (x^2 + y^2)
answer : I32
answer =
42
factorial : I32 -> I32
factorial = \n ->
List.product (List.range 1 n)
distance : { x : F64, y : F64 } -> F64
distance = \{ x, y } ->
Num.sqrt (x ^ 2 + y ^ 2)
Type Aliases
type alias Name = String
type alias Age = Int
info : (Name, Age)
info =
("Steve", 28)
type alias Point = { x : Float, y : Float }
origin : Point
origin =
{ x = 0, y = 0 }
type Name = String
type Age = Int
info :: (Name, Age)
info =
("Steve", 28)
data Point = Point { x :: Float, y :: Float }
origin :: Point
origin =
Point { x = 0, y = 0 }
Name : Str
Age : I32
info : [ Pair Name Age ]*
info =
Pair "Steve" 28
Point : { x : F64, y : F64 }
origin : Point
origin =
{ x: 0, y: 0 }
String interpolation
In all cases `toEuros` must return a string.
-- None, use concatenation
costOnSale money =
"It cost " ++ toEuros money ++ " when on sale."
-- None, use concatenation
costOnSale money =
"It cost " ++ toEuros money ++ " when on sale."
-- Alternatively, with a QuasiQuotes package
{-# LANGUAGE QuasiQuotes #-}
import NeatInterpolation
costOnSale money =
let
euros = toEuros money
in
[text|
It cost ${euros} when on sale.
|]
costOnSale = \money ->
euros = toEuros money
"It cost \(euros) when on sale."