Values and functions exercises

A few exercises relevant to values and functions.


πŸ““ Exercise: Understand parentheses in type signatures

List.zipWith is a function which takes in two lists and a function which operates on the elements in both lists sequentially until the end of one of the lists is reached.

A mysterious parenthesis blight has wiped away the necessary parentheses to communicate this.

blightedZipWith: a -> b -> c -> [a] -> [b] -> [c]

Where should the parentheses in the type signature go? Where should the implied parentheses go for this type signature?

✨ Hint
✨ Hint
threeArgumentFunction : Nat -> Text -> Boolean -> Nat

Is analogous to

threeArgumentFunction: Nat -> (Text -> (Boolean -> Nat))

It's easy to conflate the order of function application (the order in which a function is called) with the order of its type notation. Here we're looking for a description of where to draw the implied and actual parentheses for how the function arrow -> works in a type signature.

πŸ”‘ Answer
πŸ”‘ Answer

The required parentheses are as follows:

zipWith: (a -> b -> c) -> [a] -> [b] -> [c]

The first argument to List.zipWith is a function with two arguments a and b. Lists [a] and [b] are the two lists being zipped, and [c] is the return type of the over all function.

Adding the implied parentheses for the type signature yields:

zipWith: ((a -> (b -> c)) -> ([a] -> ([b] -> [c])))

πŸ““ Exercise: Understand parentheses when calling functions

A similar parenthesis blight has afflicted the site where we're calling our function. Add both the implied and necessary parentheses to the following code

use Nat
listA = [1,2,3,4,5]
listB = [2,4,6,8,10]

fixMe = List.zipWith a -> b -> a + b listA listB
πŸ”‘ Answer
πŸ”‘ Answer

The required parentheses are as follows:

use Nat
listA = [1,2,3,4,5]
listB = [2,4,6,8,10]

fixMe = List.zipWith (a -> b -> a + b) listA listB

Adding the implied parentheses for the order in which function application occurs:

use Nat
listA = [1,2,3,4,5]
listB = [2,4,6,8,10]

fixMe = ((List.zipWith (a -> b -> a + b) listA) listB)

πŸ““ Exercise: Determine a type signature for a function from how it's called

Write the signature for List.foldLeft given the following information:

An example of how foldLeft is called is

List.foldLeft (acc i -> Text.size i Nat.+ acc) 0 ["a", "bb", "ccc", "ddd"]
⧨
9

You can read about what List.foldLeft does by reading the docs linked here: List.foldLeft.doc or by entering docs List.foldLeft in the UCM.

✨ Hint
✨ Hint
Our example uses a function Text.size to transform Text into a number, and then adds the length of each text element together, but jsonschema.lib.base.data.List.foldLeft should be polymorphic--that is, it should not care if it is operating on is a List of Text, or a List of Char or a List of Boolean.
✨ Hint
✨ Hint

The first argument to jsonschema.lib.base.data.List.foldLeft is a function with two arguments, the first is the value that is being accumulated as the fold function is operating on each element on the list, the second corresponds to the element of the list.

The second argument to the jsonschema.lib.base.data.List.foldLeft function is the value that should be applied when the higher order function encounters the end of the list.

The third argument to List.foldLeft is the list being folded over.

πŸ”‘ Answer
πŸ”‘ Answer

Note, the actual signature of List.foldLeft contains a parameter in curly braces, {e}. List.foldLeft is ability polymorphic, meaning that the step function provided to it can perform effects. We'll be covering those later in our section on Abilities.

List.foldLeft : (b ->{𝕖} a ->{𝕖} b) -> b -> [a] ->{𝕖} b

πŸ““ Exercise: Debug a Unison function which fails to typecheck

brokenFactorial : Nat -> Nat
brokenFactorial n =
  use Universal Nat
  if n === 0 then 1 else n * brokenFactorial decrement n

We'll cover the use keyword later, we're using it above to disambiguate a few function names.

Something is wrong with this function. It does not typecheck, and the UCM prints out the following error. See if you can fix it.

This looks like a function call, but with a Nat where the function should be.  Are you missing an operator?

  5 |     if n === 0 then 1 else n * brokenFactorial decrement n
✨ Hint
✨ Hint
What is the order of function application in the else clause?
πŸ”‘ Answer
πŸ”‘ Answer

Due to the order of function application, we need to surround the call to Nat.decrement with parentheses.

use Nat * factorial : Nat -> Nat factorial n = if n === 0 then 1 else n * factorial (Nat.decrement n) factorial 3
⧨
6

πŸ““ Exercise: Write your own Unison function

Write a function which, when given a list, removes every other element from the list. So, ["a", "b", "c", "d"] becomes ["a", "c"]. The function should work on a list of any type.

removeEveryOther : [a] -> [a]
removeEveryOther as = base.todo "Implement here"

Some tests to validate this function are copied below, but they're also available under the ex5.tests namespace.

test> test1 = check(removeEveryOther ["a","b","c","d","e"] test.=== ["a", "c", "e"])
test> test2 = check(removeEveryOther [1] test.=== [1])
test> test3 = check(removeEveryOther [] test.=== [])
✨ Hint
✨ Hint
Check out jsonschema.lib.base.data.List.indexed for a simple way to pair each element in the list with its index.
✨ Hint
✨ Hint
Unison's modulo function is Nat.mod.
πŸ”‘ Answer
πŸ”‘ Answer

One possible answer:

removeEveryOther : [a] -> [a]
removeEveryOther : [a] -> [a]
removeEveryOther ns =
  iList = List.indexed ns
  tups = List.filter (tup -> Nat.mod (at2 tup) 2 === 0) iList
  List.map (tup -> at1 tup) tups