Muestra las diferencias entre dos versiones de la página.
| Ambos lados, revisión anterior Revisión previa Próxima revisión | Revisión previa | ||
|
wiki2:elm [2020/05/09 10:27] root [Functional programming concepts] |
wiki2:elm [2020/05/16 11:53] (actual) |
||
|---|---|---|---|
| Línea 86: | Línea 86: | ||
| "aaaaaaaaaaaaaaaaaaaa" | "aaaaaaaaaaaaaaaaaaaa" | ||
| </code> | </code> | ||
| + | |||
| + | ==== Destructuring ==== | ||
| + | |||
| + | <code> | ||
| + | let | ||
| + | (firstName, lastName) = ("John", "Doe") | ||
| + | in | ||
| + | lastName -- Result: Doe | ||
| + | |||
| + | -- ... or ... | ||
| + | second (_, snd) = snd | ||
| + | second (0, 1) -- Result: 1 | ||
| + | |||
| + | -- ... or.. | ||
| + | userName : User -> String | ||
| + | userName user = | ||
| + | let | ||
| + | (User name) = user | ||
| + | in | ||
| + | String.toLower name | ||
| + | |||
| + | -- ... or... | ||
| + | type alias Vector = { x : Int, y : Int } | ||
| + | length : Vector -> Float | ||
| + | length { x, y } = sqrt <| toFloat <| x^2 + y^2 | ||
| + | length { x = 1, y = 2 } -- Result: 2.2360 | ||
| + | length { x = 1, y = 2, z = 3 } -- Error | ||
| + | -- To do this: | ||
| + | length : { r | x : Int, y : Int } -> Float | ||
| + | length { x, y } = sqrt <| toFloat <| x^2 + y^2 | ||
| + | length { x = 1, y = 2, z = 3 } -- OK | ||
| + | -- or | ||
| + | type alias Vector r = { r | x : Int, y : Int } | ||
| + | length : Vector r -> Float | ||
| + | length { x, y } = sqrt <| toFloat <| x^2 + y^2 | ||
| + | </code> | ||
| + | |||
| + | ==== Pattern matching ==== | ||
| + | Pattern matching is a mechanism for choosing the branch of code to execute based on the type or value of a given expression. | ||
| + | |||
| + | ==== Functors, monads and applicatives ==== | ||
| + | |||
| + | * Functors are things you can map on | ||
| + | * Monads things you can andThen on | ||
| + | * Applicatives things you can andMap on | ||
| ===== Basic ===== | ===== Basic ===== | ||
| + | ==== Records ==== | ||
| + | |||
| + | Constructing: | ||
| + | <code> | ||
| + | type alias User = { userId: Int, name: String } | ||
| + | User 1 "John" | ||
| + | </code> | ||
| + | |||
| + | For each record type alias, the compiler automatically generates a constructor function of the same name. The constructor is a regular function so it can be partially applied: | ||
| + | <code> | ||
| + | User 1 | ||
| + | </code> | ||
| + | |||
| + | Extending records in the next two ways: | ||
| + | <code> | ||
| + | type alias GenericNode a = { a | | ||
| + | actualLoops : Int, actualRows : Int, | ||
| + | actualStartupTime : Float, | ||
| + | actualTotalTime : Float | ||
| + | } | ||
| + | type alias CteNode = GenericNode | ||
| + | { | ||
| + | nameAlias : String | ||
| + | } | ||
| + | -- A CteNode is a record with all the fields of GenericNode plus an additional alias field. | ||
| + | </code> | ||
| + | |||
| + | === Constructing records from another === | ||
| + | <code> | ||
| + | > type alias A = { a: String, b: Int } | ||
| + | > type alias B = { a: String, b: Int } | ||
| + | > a : A | ||
| + | | a = A "prueba" 666 | ||
| + | { a = "prueba", b = 666 } : A | ||
| + | > b : B | ||
| + | | b = a | ||
| + | { a = "prueba", b = 666 } : B | ||
| + | </code> | ||
| ==== Type annotation ==== | ==== Type annotation ==== | ||
| Línea 163: | Línea 246: | ||
| -- Visitor "kate95" : User | -- Visitor "kate95" : User | ||
| </code> | </code> | ||
| + | |||
| + | === Types variable === | ||
| + | |||
| + | When you do not care about the type that is passed. In the next example the ''List'' passed to ''List.length'' can be a ''List String'', ''List Int''... | ||
| + | <code> | ||
| + | > List.length | ||
| + | <function> : List a -> Int | ||
| + | </code> | ||
| + | |||
| + | However it can restrict the output. With ''List.reverse'' we know that we are going to obtain a ''List'' with the same type as it was passed on the first instance. | ||
| + | <code> | ||
| + | > List.reverse | ||
| + | <function> : List a -> List a | ||
| + | > List.reverse [ "a", "b", "c" ] | ||
| + | ["c","b","a"] : List String | ||
| + | > List.reverse [ True, False ] | ||
| + | [False,True] : List Bool | ||
| + | </code> | ||
| + | |||
| + | === Recursive types === | ||
| + | <code> | ||
| + | type Tree a | ||
| + | = Empty | ||
| + | | Node a (List (Tree a)) | ||
| + | </code> | ||
| + | |||
| + | === Special types === | ||
| + | * number (Int, Float) | ||
| + | * appendable (String, Lists) | ||
| + | * comparable (Int , Float , Char , String , lists, and tuples) | ||
| + | |||
| + | Consider ''Set.map'' from the Elm core library: | ||
| + | <code> | ||
| + | map : (comparable -> comparable2) -> Set comparable -> Set comparable2 | ||
| + | map func set = | ||
| + | </code> | ||
| + | This function produces a new set (in other words: list) from the input set by applying ''func'' to elements of ''set''. Since set elements have to be comparable, ''func'' takes a value of a ''comparable'' type as its argument, and it also has to return a value of a ''comparable'' type. | ||
| + | |||
| + | However, the input and output types are not necessarily the same, so it doesn’t make sense to write ''(comparable -> comparable)''. In order to allow these sorts of functions, number, appendable and comparable can be suffixed with an alphanumeric sequence without changing their meaning. | ||
| + | |||
| + | === Phantom types === | ||
| + | |||
| + | Those types that contain variables that are not used on their constructors: | ||
| + | <code> | ||
| + | type Unit tag value = Unit value | ||
| + | </code> | ||
| + | |||
| + | It can be used like this: | ||
| + | <code> | ||
| + | type KmTag = KmTag | ||
| + | type MileTag = MileTag | ||
| + | km : number -> Unit KmTag number | ||
| + | km = Unit | ||
| + | mile : number -> Unit MileTag number | ||
| + | mile = Unit | ||
| + | </code> | ||
| + | These types use different tags. Now we can restrict value types; for example, when a function expects a KmTag and we pass a mile to it, then an error will raise. | ||
| + | <code> | ||
| + | showKm : Unit KmTag Float -> String | ||
| + | showKm (Unit d) = String.fromFloat d | ||
| + | distance = mile 12.34 | ||
| + | s = showKm distance -- this raises an error | ||
| + | </code> | ||
| + | |||
| + | Use cases: | ||
| + | * When you want to distinguish between values which have the same runtime representation, and you have a shared set of operations you want to perform on all the different types of these values. | ||
| + | * "hen you want to use a particular runtime representation for reasons of performance or memory use, but still want compile-time enforcement of the semantics of different types. | ||
| + | |||
| ==== Type Aliases ==== | ==== Type Aliases ==== | ||