import Prelude unless otherwise noted.
!!! tip “REPL or module‽”
If you see `>` as the first character in a line, it means we are in the REPL, otherwise, assume it is in a module.
!!! danger “Bad function names‽”
Some of these functions have non-meaningful names on purpose. The idea is that we know what a function does by careful scrutiny of the signature and implementation as a way to force ourselves to read and understand each bit. If we say `add1 = ...`, we immediately know this functions adds 1 to its argument. If we name it `f` or `g`, we have to read the signature and implementation carefully to understand its ideas and what it does. That is a terrible idea for production code, but a very good approach to study, practice, ponder about stuff, and *learn*.
Applying a lambda expression right then and there:
> (\n -> (mod n 2)) 6 0 > (\n -> (n `mod` 2)) 5 0
Note that functions are not printable; they do not have an instance of the type class
(\n -> n) # (1)
It produces an error that we cannot “print” something that does not implement
> (\n -> n) Error found: in module $PSCI at <internal>:0:0 - 0:0 (line 0, column 0 - line 0, column 0) No type class instance was found for Data.Show.Show (t2 -> t2) The instance head contains unknown type variables. Consider adding a type annotation. while solving type class constraint PSCI.Support.Eval (t2 -> t2) while applying a function eval of type Eval t1 => t1 -> Effect Unit to argument it while checking that expression eval it has type Effect t0 in value declaration $main where t0 is an unknown type t1 is an unknown type t2 is an unknown type See https://github.com/purescript/documentation/blob/master/errors/NoInstanceFound.md for more information, or to contribute content related to this error.
Assigning a lambda expression to a variable:
> f = (\n -> mod n 2) > f 3 1
module Data.Examples where import Prelude isEven :: Int -> Boolean isEven n = mod n 2 == 0 f1 :: Int -> Boolean f1 = eq 0 <<< (_ `mod` 2)
Verifying if a number is even:
## Aliasing functions to infix symbol > f1 n = eq 0 (mod n 2) > f2 n = eq (mod n 2) 0 > f1 4 true > f2 4 true
0 with the application
(mod n 2). In
eq (mod n 2) with
Aliasing functions to infix symbol#
Making an alias to
infixr 4 mod as % f2 :: Int -> Boolean f2 = eq 0 <<< (_ % 2) f3 :: Int -> Boolean f3 = (_ % 2) >>> eq 0
Currying and Partial Application#
Let’s define a function that “takes two arguments”:
f :: Int -> Int -> Int f x y = (+) x y
f is curried by default. It is the default PureScript (and Haskell) behavior that all functions are curried by default (unless you make some tuple magic to de-curry a function).
The Quintessential “add 1” Example#
f does NOT take two arguments. It takes one argument, and returns a function that takes the other argument, which then returns the final, sum result.
The add 1 quintessential partial application example:
> :type f Int -> Int -> Int > g = f 1 > :type g Int -> Int > g 5 6
f, that is, pass one argument. It returns a function with that argument already applied.
g now is the partially applied
As we see,
increment has the value 1 partially (or pre) applied, so, when we later apply
increment 5, the body of the function
(+) x y becomes
(+) 1 5 and therefore the result 6.
We can apply all arguments at once, but that just seems like “all at once”:
> f 1 2 3
But this is what is really happening (more or less 😅)
> (f 1) 2 3 > f 1 $ 2 3
!!! tip “Currying and Partial Application”
When we define a function, we say it is a curried function if it has this property of not requiring all arguments at once upon application. PureScript and Haskell functions are curried by default. No especial syntax or anything else is needed to get curried functions. So, **currying** happens (automatically) when we define functions. After a function exists, we can *apply the function to arguments*. If we apply less than the total number of arguments the function requires to be fully applied, we say we *partially applied the function*. Therefore, **partial application** happens when applying (invoking, calling) the function (if less than the total number of argument a function requires to be fully applied are provided). A partially applied function returns a function which some of the parameters applied, still awaiting for the remaining parameters to fully realize the function application, which then produces the final value or result. Also note that the returned function from a partial application is itself curried.
Example with replace#
With Object Oriented languages with create specializations from generalizations mostly through the use of inheritance and interfaces. In functional languages, we do this mostly through composition and partial application.
Consider the function
replace from the
> import Data.String > :type replace Pattern -> Replacement -> String -> String > replace (Pattern " ") (Replacement "-") "Tomb Raider I 1996" "Tomb-Raider I 1996"
replace replaces any
Pattern with some
Replacement. We could make it more specialized by partially applying its
> replaceSpaces = replace (Pattern " ") > :type replaceSpaces Replacement -> String -> String > replaceSpaces (Replacement "-") "Tomb Raider I 1996" "Tomb-Raider I 1996"
Now, the function
replaceSpaces is a specialized version of the more generic
replace, in which it always replaces spaces with some
We could further specialize
replace by partially applying the first two arguments. In this case, the
Pattern and the
Replacement specialize the function, and the remaining argument is the
String to which the substitution will be performed on:
> replaceSpacesWithHyphen = replace (Pattern " ") (Replacement "-") > :type replaceSpacesWithHyphen String -> String > replaceSpacesWithHyphen "Tomb Raider I 1996" "Tomb-Raider I 1996"
replaceSpaces exist, we could specialize from that instead of from the original
> replaceSpaces = replace (Pattern " ") > replaceSpacesWithHyphen = replaceSpaces (Replacement "-") > replaceSpacesWithHyphen "Tomb Raider I 1996" "Tomb-Raider I 1996"
Here’s one example using
replaceAll with proper type signatures:
import Data.String.Pattern (Pattern(..), Replacement(..)) import Data.String.Common (replaceAll) replaceSpaces :: Replacement -> String -> String replaceSpaces r s = replaceAll (Pattern " ") r s replaceSpacesWithHyphens :: String -> String replaceSpacesWithHyphens s = replaceSpaces (Replacement "-") s