# Exercises using abilities

If you need some inspiration for using and writing abilities, we've curated a list of exercises fromthe Unison language Exercism trackthat either benefit from the one or more of the abilities found inbase,or that include stubs for writing your own ability handlers. The descriptions below make note of how the problem might benefit from using abilities, while full instructions, tests, and stub files can be found by following the links in each section!

๐Majority element

The majority element is one which takes up over half of the space in a collection. Given a list of elements representing the colors, return the color found as the majority, otherwise indicate that no majority has been found.

While you can implement the function under test in any way you choose, we've provided a few stubs which use theStoreability. For additional practicewritinghandlers, we've given a handler stub calledrunWithto implement.runWithreturns both the result of running the function which uses the Store ability and the final state of the Store.

This exercise is about implementing basic Stream ability operations. Writing your own handlers for common functional combinators likemap,flatMapandfilter.

๐Additional challenge, incorporate theAskability inStream.pipe
๐Additional challenge, incorporate theAskability inStream.pipe
structural ability Ask a
structural ability Ask a where lib.base.abilities.Ask.ask : {Ask a} a

Here's another example ability,Ask,which provides one operation,ask,for requesting a value of typeafrom whatever code handles the ability.

provideis a handler for ask which provides a constant value.

A common usage ofAskis to avoid needing to pass around common configuration settings, justprovide myConfig 'myMainto make myConfig available anywhere inmyMainwith a call toask.

provide 10 '(1 Nat.+ ask Nat.+ ask)โงจ21

Computations that useAskcan also be thought of as stream consumers. Try writing a functionpipe,which can be used to statefully transform a stream:

abilityExercises.Stream.pipe : '{Stream a} ()
-> '{Ask a, Stream b} r
-> '{Stream b} ()
abilityExercises.Stream.pipe :
'{Stream a} () -> '{Ask a, Stream b} r -> '{Stream b} ()
abilityExercises.Stream.pipe = todo "implement me"

In implementing this, you'll have a handler that matches on aRequest {Ask a, Stream b} r.Handlers that match on multiple abilities at once like this are sometimes called "multihandlers." There's nothing special you need to do in Unison to write multihandlers; just match on the operations from more than one ability in your handler!

Once you've writtenabilityExercises.Stream.pipe,try writingStream.map,Stream.filter,andStream.takeusingabilityExercises.Stream.pipe.

Given a string containing brackets[],braces{},parentheses(),or any combination thereof, verify that any and all pairs are matched and nested correctly.

There are many ways to accomplish this exercise, but we've provided some additional stubs for folks who'd like to practice writing their own ability handlers.

The signature for thecheckBalancehelper function given for this exercise makes use of an ability requirement,{Stack}.The Stack ability is defined for you, but the handler,Stack.runis not! If you chose, you can implement the Stack.run, checkBalance, and isPaired function. The tests will run even if you choose not to implement this exercise with abilities.

๐Change

Correctly determine the fewest number of coins to be given to a customer such that the sum of the coins' value would equal the correct amount of change.

This exercise doesn't explicitly require abilities, but many folks have used theStoreability for memoization when optimizing their solution.

๐Zipper

Write a Zipper that allows you to navigate through a binary tree data structure.

There are many ways to accomplish this exercise, but we've tailored this exercise for folks who'd like to practice writing their own Zipper ability.

The Zipper ability itself is defined for you, but you'll need to implement the handler that allows it to navigate through the given binary tree data structure.

๐Zebra Puzzle

Given a set of possible attributes of a number of houses and a series of constraints, find the house that whose resident owns a zebra.

This problem offers an opportunity to use theEachability for non-determinism in thebaselibrary.

You might also want choose to use an ability for optimizing the search space when finding who owns the zebra. ๐ค

## More exercises using abilities

๐Implement functions with calls to Ability operations

Given the type signatures below, implement the functions to satisfy the compiler using the request operations of the Ability or functions found in.base

getWithAbort : a -> Map a b ->{Abort} b
getWithAbort key map = base.todo "implement me!"
โจHint: how do I see the request operations of an ability?
โจHint: how do I see the request operations of an ability?
In the UCM, you can enterview Abortto see the ability definition. The function signatures after thewherekeyword are the request operations.

Using theOptional.toAbortfunction.

getWithAbort : a -> Map a b ->{Abort} b
getWithAbort key map = toAbort (get key map)

Pattern matching onOptional

getWithAbort : a -> Map a b ->{Abort} b
getWithAbort key map =
match get key map with
Some a -> a
None   -> abort

๐Implement functions with Ability operations

rangeAverage : Nat ->{Store [Nat]} Float
rangeAverage n = base.todo "implement me"

Using theStoreability to keep track of aListof numbers, write a function which takes in some numbernofNatas an upper bound of a list from0up to (but not including)nand returns the average of the final list.

So, given an upper value of5,the average produced would be the average of[0, 1, 2, 3, 4],or2.0

One possible solution is as follows:

averageOfRange : Nat ->{Store [Nat]} Float
averageOfRange : Nat ->{Store [Nat]} Float
averageOfRange n =
use Float / fromNat
use List +:
use Nat + ==
go = cases
i
| i == n     ->
myList = Store.get
listSize = List.size myList
sum =
lib.base.data.List.foldLeft
(acc element -> element + acc) 0 myList
fromNat sum / fromNat listSize
| otherwise  ->
Store.modify (currentList -> i +: currentList)
go (i + 1)
go 0

๐Apply a handler to a function requiring abilities

Check out the functions in thebaselibraries for operating onStoreand see if you can apply a handler to the function you wrote previously forThe handler should help you return theFloatvalue to check your function.

โจHint: What kind of function am I looking for?
โจHint: What kind of function am I looking for?
You'll want a function whose return type does not requireStorein curly braces in its final value. The handler should run or "interpret" the program which performs aStoreability into another value.

One possible solution is as follows:

handleAverageOfRange : Float
handleAverageOfRange : Float
handleAverageOfRange = withInitialValue [] '(averageOfRange 5)

withInitialValuehandles a function that requires theStoreability by setting the initial state of theStore.

๐Debug an abilities issue

The following watch expression is failing to typecheck.

randomIndex : [Nat] -> '{Random, Abort} Nat
randomIndex list = 'let
n = List.size list
if n === 0 then abort else Random.natIn 0 n

> toOptional! (Random.lcg 7 (randomIndex [1,2,3]))

The UCM error is:

The 2nd argument to (<|)

has type:  Nat
but I expected:  Unit ->{g, Abort} ๐ฉ

142 | randomIndex : [Nat] -> '{Random, Abort} Nat
143 | randomIndex list = 'let
144 |   n = List.size list
145 |   if n === 0 then abort else Random.natIn 0 n
146 |
147 | > toOptional! (Random.lcg 7 (randomIndex [1,2,3]))

Why is the error occurring and how might it be corrected?

randomIndex : [Nat] -> '{Abort, Random} Nat
toOptional! '(lib.base.abilities.Random.lcg 7 (randomIndex [1, 2, 3]))โงจSome 2
toOptional!is a function which expects a delayed computation which performs theAbortability. The entire expression will need a'symbol in front of the call tolib.base.abilities.Random.lcg