Values and functions exercises

A few exercises relevant to values and functions.


๐Ÿ““Exercise: Understand parentheses in type signatures

List.zipWithis 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 functionapplication(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 toList.zipWithis a function with two argumentsaandb.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 forlib.base.data.List.foldLeftgiven the following information:

An example of howfoldLeftis called is

lib.base.data.List.foldLeft (acc i -> Text.size i Nat.+ acc) 0 ["a", "bb", "ccc", "ddd"]
โงจ
9

You can read about whatlib.base.data.List.foldLeftdoes by reading the docs linked here:base.data.List.foldLeft.docor by enteringdocs List.foldLeftin the UCM.

โœจHint
โœจHint
Our example uses a functionText.sizeto transformTextinto a number, and then adds the length of each text element together, butlib.base.data.List.foldLeftshould 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 tolib.base.data.List.foldLeftis 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 thelib.base.data.List.foldLeftfunction 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 oflib.base.data.List.foldLeftcontains a parameter in curly braces,{e}.lib.base.data.List.foldLeftisability polymorphic,meaning that the step function provided to it can perform effects. We'll be covering those later in our section on Abilities.

lib.base.data.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 theusekeyword 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 theelseclause?
๐Ÿ”‘Answer
๐Ÿ”‘Answer

Due to the order of function application, we need to surround the call toNat.decrementwith 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 theex5.testsnamespace.

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 outlib.base.data.List.indexedfor a simple way to pair each element in the list with its index.
โœจHint
โœจHint
Unison's modulo function isNat.mod.
๐Ÿ”‘Answer
๐Ÿ”‘Answer

One possible answer:

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