]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.scl.tutorial/scl/Tutorial/1.06 Types.md
Import org.simantics.scl.tutorial from incubator SVN repo
[simantics/platform.git] / bundles / org.simantics.scl.tutorial / scl / Tutorial / 1.06 Types.md
diff --git a/bundles/org.simantics.scl.tutorial/scl/Tutorial/1.06 Types.md b/bundles/org.simantics.scl.tutorial/scl/Tutorial/1.06 Types.md
new file mode 100644 (file)
index 0000000..2b1e69a
--- /dev/null
@@ -0,0 +1,178 @@
+# Types\r
+\r
+SCL is statically typed language which means that types of the possible values of all variables are known already\r
+at compile time. The following types (or more exactly, type constructors) have builtin support:\r
+\r
+* `Boolean`\r
+* `Byte`, `Short`, `Integer`, `Long`\r
+* `Float`, `Double`\r
+* `String`\r
+* `[]`\r
+* `()`, `(,)`, `(,,)`, ...\r
+* `Maybe`\r
+* `Vector`\r
+\r
+Other type constructors are either imported from the host language or defined in SCL modules.\r
+Except for the couple of special cases in the previous list, the names of all type constructors are capitalized.\r
+\r
+Some type constructors are parametric (compare to generics in Java or templates in C++).\r
+For example, the list type constructor `[]` has one parameter: the type of the list elements.\r
+Thus `[Integer]` is the type of the integer lists and `[String]` is the type of string lists.\r
+`[[Integer]]` is the type of the lists of integer lists. Parameters are usually\r
+written after the parametric type constructor: for example `Maybe String` or `Vector Integer`, but\r
+some of the builtin type constructors can be written in a special way in order to make the type\r
+expressions more readable:\r
+\r
+~~~\r
+[a] = [] a\r
+(a,b) = (,) a b\r
+(a,b,c) = (,,) a b c\r
+...\r
+~~~\r
+\r
+Particularly important type constructor is `->` for building function types. For example, the type\r
+of the function computing the length of a string is `String -> Integer`: the function takes a string\r
+as a parameter and returns an integer.\r
+\r
+Types of the functions taking multiple parameters are written by composing function types. For example,\r
+the type of a function taking nth element of a string list is `[String] -> Integer -> String`. The function\r
+takes a string list and an integer as a parameter and returns a string. Function type operator `->`\r
+is right associative thus the previous type is equivalent to `[String] -> (Integer -> String)`.\r
+Thus the type expression can be read as well as a type of functions taking a string list and returning another\r
+function from integers and strings.\r
+\r
+`(a,b)` is the type of pairs where the first component of the pair has type `a` and the second component has type `b`.\r
+Tuple types `(a,b,c)`, `(a,b,c,d)` etc. are defined similarly. `Maybe a` is the type of optional values.\r
+\r
+## Type annotations\r
+\r
+There are two kind of type annotations in SCL, both use `::` to separate\r
+the value from the type.\r
+\r
+Top-level annotations give a type for top-level definitions and they\r
+can be used only in SCL modules:\r
+\r
+    flip :: (a -> b -> <e> c) -> b -> a -> <e> c\r
+    flip f x y =  f y x\r
+\r
+Inline annotations may be embedded in expressions or patterns:\r
+\r
+    execute (print (getVar "PI1#PI12_PRESSURE" :: Double))\r
+\r
+SCL compiler can usually infer the type of the expressions but there\r
+are couple of reasons to use annotations. They\r
+\r
+* document the definitions\r
+* prevents some typos that would change the type\r
+* restrict the type to be more specific than what the compiler can infer\r
+* prevents the compiler from making a bad guess for the type\r
+\r
+## Type variables\r
+\r
+Many functions can be defined so that they do not need to know the exact types they are operating with.\r
+Such unknown types can be filled in type expressions by type variables: for example the function we\r
+discussed earlier that took nth element of a string list does not need the information that list elements\r
+are strings in its implementation and so it can be given a more generic type `[a] -> Integer -> a`,\r
+i.e a function taking a list of `a`:s and an integer as a parameter and returning a single `a`.\r
+\r
+Function types with type variables tell quite much about the function assuming it is total,\r
+i.e does not hang or throw a runtime exception with any parameters. For example the type\r
+signatures define the following functions uniquely:\r
+\r
+~~~\r
+id :: a -> a\r
+swap :: (a,b) -> (b,a)\r
+const :: a -> b -> a\r
+~~~\r
+\r
+and there are only two possible total functions satisfying the following signature\r
+\r
+~~~\r
+choose :: a -> a -> a\r
+~~~\r
+\r
+Type variables may also refer to parametric types, but all useful examples involve type constraints we describe below.\r
+\r
+## Type constraints\r
+\r
+SCL does not support function overloading at least in the way Java and C++ support it.\r
+This means that every function has a unique type signature. Now consider the addition function `(+)`.\r
+We could defined its signature for example as `Integer -> Integer -> Integer`,\r
+but then we would need some other name for the sum of doubles `Double -> Double -> Double`\r
+or strings `String -> String -> String`. It would seem like the right signature would be\r
+`a -> a -> a`, but that is not satisfactory either, because then the function had to\r
+somehow support all possible types. \r
+\r
+The solution to the problem is to constraint the set of allowed types for the type variable.\r
+Such a constrained type is written in the case of addition function as \r
+`Additive a => a -> a -> a`. The constraint `Additive a` is composed of a type class\r
+`Additive` followed by parameters of the constraint. The type can be understood in\r
+two different ways. A logical reading is\r
+> for any additive type `a`, the function takes two `a`:s as parameters and returns `a`\r
+and operational reading that gives a good intuition what is happening in runtime\r
+> the function takes a parameter `Additive a` that describes a set of methods that\r
+> can be used to operate values of `a` and two `a`:s and returns `a`\r
+\r
+Constrained type variable can be also parametric. For example, let's say we want\r
+to define a function `getNth` that takes nth element of a list but also works with arrays.\r
+The signature would then be\r
+\r
+~~~\r
+getNth :: Sequence s => s a -> Integer -> a\r
+~~~\r
+\r
+Type classes form a hierarchy: each class may have any number of superclasses.\r
+Only the most specific type class is needed in the constraints. For example,\r
+addition and multiplication have the following signatures:\r
+\r
+~~~\r
+(+) :: Additive a => a -> a -> a\r
+(*) :: Ring a => a -> a -> a\r
+~~~\r
+\r
+and `Additive a` is a superclass of `Ring a`. A function using both operators need to specify only `Ring a` as a constraint:\r
+\r
+~~~\r
+doubleAndAddOne :: Ring a => a -> a\r
+doubleAndAddOne x = 2*x + 1\r
+~~~\r
+\r
+## Effect types\r
+\r
+SCL functions are [referentially transparent]("http://en.wikipedia.org/wiki/Referential_transparency_(computer_science)")\r
+which means that they are like mathematical functions always returning the same value for the same\r
+parameters and not causing any observable side-effects. This seems extremely restrictive\r
+because most of the programs are written in order to generate some kind of side-effects and even\r
+completely mathematical algorithms involve functions that are not referentially transparent such as\r
+generation of random numbers.\r
+\r
+SCL manages side-effects with *effect types*.\r
+An effectful computation has type `<effects> t`.\r
+The type after angle brackets is the type of the value returned by the computation.\r
+Side-effects that might happen during computation are listed inside of the angle brackets.\r
+Effect types must always occur as a return type of a function. Here are some examples of functions with side-effects:\r
+\r
+~~~\r
+randomBetween :: Double -> Double -> <Nondet> Double\r
+resourceByUri :: String -> <ReadGraph> Resource\r
+claim :: Resource -> Resource -> Resource -> <WriteGraph> ()\r
+randomLiteral :: Double -> Double -> <Nondet,WriteGraph> Resource\r
+~~~\r
+\r
+Effect types are much like type constraints: you can mostly ignore them when using functions.\r
+All effects you use are automatically collected and added to the type signature of the defined\r
+function (or checked against type annotation if provided).\r
+\r
+Like type classes, effects form a hierarchy. For example `WriteGraph` inherits `ReadGraph`\r
+and a function both reading and writing to graph is annotated only with `<WriteGraph>` effect.\r
+\r
+Like types, effects can also be abstracted with effect variables. This is important when\r
+defining generic functions that manipulate possibly effectful functions:\r
+\r
+~~~\r
+executeTwice :: (() -> <e> ()) -> <e> ()\r
+executeTwice f = { f () ; f () }\r
+\r
+(.) :: (b -> <e2> c) -> (a -> <e1> b) -> (a -> <e1,e2> c)\r
+(f . g) x = f (g x)\r
+~~~
\ No newline at end of file