Polyvariadic Functions in Haskell - haskell

After reading this article on writing polyvariadic functions in Haskell, I tried to write some of my own.
At first I thought I'd try to generalize it - so I could have a function that returned variadic functions by collapsing arguments as given.
{-# OPTIONS -fglasgow-exts #-}
module Collapse where
class Collapse a r | r -> a where
collapse :: (a -> a -> a) -> a -> r
instance Collapse a a where
collapse _ = id
instance (Collapse a r) => Collapse a (a -> r) where
collapse f a a' = collapse f (f a a')
However, the compiler didn't like that:
Collapse.hs:5:9:
Functional dependencies conflict between instance declarations:
instance Collapse a a -- Defined at Collapse.hs:5:9-20
instance (Collapse a r) => Collapse a (a -> r)
-- Defined at Collapse.hs:7:9-43
If I went back and added a wrapper type for the final result, however, it worked:
module Collapse where
class Collapse a r | r -> a where
collapse :: (a -> a -> a) -> a -> r
data C a = C a
instance Collapse a (C a) where
collapse _ = C . id
instance (Collapse a r) => Collapse a (a -> r) where
collapse f a a' = collapse f (f a a')
sum :: (Num a, Collapse a r) => a -> r
sum = collapse (+)
Once I made this change, it compiled fine, and I could use the collapse function in ghci.
ghci> let C s = Collapse.sum 1 2 3 in s
6
I'm not sure why the wrapper type is required for the final result. If anyone could explain that, I'd highly appreciate it. I can see that the compiler's telling me that it's some issue with the functional dependencies, but I don't really grok the proper use of fundeps yet.
Later, I tried to take a different tack, and try and define a variadic function generator for functions that took a list and returned a value. I had to do the same container trick, and also allow UndecidableInstances.
{-# OPTIONS -fglasgow-exts #-}
{-# LANGUAGE UndecidableInstances #-}
module Variadic where
class Variadic a b r | r -> a, r -> b where
variadic :: ([a] -> b) -> r
data V a = V a
instance Variadic a b (V b) where
variadic f = V $ f []
instance (Variadic a b r) => Variadic a b (a -> r) where
variadic f a = variadic (f . (a:))
list :: Variadic a [a] r => r
list = variadic . id
foldl :: (Variadic b a r) => (a -> b -> a) -> a -> r
foldl f a = variadic (Prelude.foldl f a)
Without allowing UndecidableInstances the compiler complained that my instance declarations were illegal:
Variadic.hs:7:0:
Illegal instance declaration for `Variadic a b (V b)'
(the Coverage Condition fails for one of the functional dependencies;
Use -XUndecidableInstances to permit this)
In the instance declaration for `Variadic a b (V b)'
Variadic.hs:9:0:
Illegal instance declaration for `Variadic a b (a -> r)'
(the Coverage Condition fails for one of the functional dependencies;
Use -XUndecidableInstances to permit this)
In the instance declaration for `Variadic a b (a -> r)'
However, once it compiled, I could successfully use it in ghci:
ghci> let V l = Variadic.list 1 2 3 in l
[1,2,3]
ghci> let vall p = Variadic.foldl (\b a -> b && (p a)) True
ghci> :t vall
vall :: (Variadic b Bool r) => (b -> Bool) -> r
ghci> let V b = vall (>0) 1 2 3 in b
True
I guess what I'm looking for is an explanation of why the container type for the final value is necessary, as well as why all the various functional dependencies are necessary.
Also, this seemed odd:
ghci> let vsum = Variadic.foldl (+) 0
<interactive>:1:10:
Ambiguous type variables `a', `r' in the constraint:
`Variadic a a r'
arising from a use of `Variadic.foldl' at <interactive>:1:10-29
Probable fix: add a type signature that fixes these type variable(s)
<interactive>:1:10:
Ambiguous type variable `a'in the constraint:
`Num a' arising from the literal `0' at <interactive>:1:29
Probable fix: add a type signature that fixes these type variable(s)
ghci> let vsum' = Variadic.foldl (+)
ghci> :t vsum'
(Num a, Variadic a a r) => t -> a -> r
ghci> :t vsum' 0
(Num a, Variadic a a r) => a -> r
ghci> let V s = vsum' 0 1 2 3 in s
6
I'm guessing that's fallout from allowing UndecidableInstances, but I don't know, and I'd like to better understand what's going on.

The idea behind functional dependencies is that in a declaration like
class Collapse a r | r -> a where
...
the r -> a bit says that a is uniquely determined by r. So, you can't have instance Collapse (a -> r) (a -> r) and instance Collapse a (a -> r). Note that instance Collapse (a -> r) (a -> r) follows from instance Collapse a a for the complete picture.
In other words, your code is trying to establish instance Collapse t t (the type variable's name is of course unimportant) and instance Collapse a (a -> r). If you substitute (a -> r) for t in the first instance declaration, you get instance Collapse (a -> r) (a -> r). Now this is the only instance of Collapse with the second type parameter equal to (a -> r) that you can have, because the class declaration says that the first type parameter is to be deducible from the second. Yet next you try to establish instance a (a -> r), which would add another instance of Collapse with the second type parameter being (a -> r). Thus, GHC complains.

If you're still experimenting with this, here's an example of constructing a polyvariadic function from a function taking a list, without requiring either a wrapper type or undecidable instances:
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}
class Variadic a b r | r -> a where
variadic :: ([a] -> b) -> r
instance Variadic a b (a -> b) where
variadic f x = f [x]
instance (Variadic a b (a -> r)) => Variadic a b (a -> a -> r) where
variadic f x y = variadic (f . (x:)) y
vList :: (Variadic a [a] r) => r
vList = variadic id
vFoldl :: (Variadic b a r) => (a -> b -> a) -> a -> r
vFoldl f z = variadic (foldl f z)
vConcat :: (Variadic [a] [a] r) => r
vConcat = vFoldl (++) []
main = do
putStrLn $ vConcat "abc" "def" "ghi" "jkl"
putStrLn $ vList 'x' 'y' 'z'
if vFoldl (&&) True True True True then putStrLn "Yes" else putStrLn "No"
if vFoldl (&&) True True False True then putStrLn "Yes" else putStrLn "No"
The downsides to this approach are that the type checker must be able to infer the type of the result (or you have to annotate it), and that it fails badly on polymorphic numeric constants; the reasons for both problems are discussed in the article you mentioned. Don't know if you'll find that helpful, but I was tinkering with polyvariadic functions earlier and remembered this question.

Michał Marczyk is absolutely correct about the fundeps and instance matching issue, and the wrapper type seems like an easy fix. On the other hand, if you're already reading Oleg's site, you might prefer to go deeper down the rabbit hole and try writing an instance for "any type that isn't a function".
As far as UndecidableInstances goes, the coverage condition is described here; it should be obvious why your instances fail it. Note that the word "undecidable" here means undecidable in roughly the same sense as in "the Halting Problem is undecidable"--that is to say, you're telling GHC to recklessly attempt to resolve code that could send it into an infinite loop based only on your assertion that it's okay. It's fun for hacking neat ideas, but consenting to be a human first-pass type-checker for GHC is a burden I personally find wearying.

Related

Matching higher-kinded types in SYB

In general, I'm wondering if there's a way to write a generic fold that generalizes a function that applies a forall type like:
f :: forall a. Data (D a) => D a -> b
given some datatype D for which instance Data (D a) (possibly with constraints on a). To be concrete, consider something even as simple as False `mkQ` isJust, or generally, a query on the constructor of a higher-kinded datatype. Similarly, consider a transformation mkT (const Nothing) that only affects one particular higher-kinded type.
Without explicit type signatures, they fail with No instance for Typeable a0, which is probably the monomorphism restriction at work. Fair enough. However, if we add explicit type signatures:
t :: GenericT
t = mkT (const Nothing :: forall a. Data a => Maybe a -> Maybe a)
q :: GenericQ Bool
q = False `mkQ` (isJust :: forall a. Data a => Maybe a -> Bool)
instead we are told that the forall type of the outer signatures are ambiguous:
Could not deduce (Typeable a0)
arising from a use of ‘mkT’
from the context: Data a
bound by the type signature for:
t :: GenericT
The type variable ‘a0’ is ambiguous
I can't wrap my head around this. If I'm really understanding correctly that a0 is the variable in t :: forall a0. Data a0 => a0 -> a0, how is it any more ambiguous than in say mkT not? If anything, I would've expected mkT to complain because it is the one that interacts with isJust. Additionally, these functions are more polymorphic than the branching on concrete types.
I'm curious to know if this is a limitation of proving the inner constraint isJust :: Data a => ... — my understanding is that any type of instance Data inhabited with Maybe a must also have Data a to be valid by the instance constraint instance Data a => Data (Maybe a).
tldr: You need to create a different function.
mkT has the following signature:
mkT :: (Typeable a, Typeable b) => (a -> a) -> (b -> b)
And you want to apply it to a polymorphic function of type (forall x. Maybe x -> Maybe x). It is not possible: there is no way to instantiate a in (a -> a) to obtain (forall x. Maybe x -> Maybe x).
It's not just a limitation of the type system, the implementation of mkT wouldn't support such an instantiation either.
mkT simply compares concrete types a and b for equality at run time. But what you want is to be able to test whether b is equal to Maybe x for some x. The logic this requires is fundamentally more involved. But it is certainly still possible.
Below, mkT1 first matches the type b against the App pattern to know whether b is some type application g y, and then tests equality of g and f:
{-# LANGUAGE ScopedTypeVariables, RankNTypes, TypeApplications, GADTs #-}
import Type.Reflection
-- N.B.: You can add constraints on (f x), but you must do the same for b.
mkT1 :: forall f b. (Typeable f, Typeable b) => (forall x. f x -> f x) -> (b -> b)
mkT1 h =
case typeRep #b of
App g y ->
case eqTypeRep g (typeRep #f) of
Just HRefl -> h
_ -> id
_ -> id
Compilable example with mkQ1 as well:
{-# LANGUAGE ScopedTypeVariables, RankNTypes, TypeApplications, GADTs #-}
import Type.Reflection
mkT1 :: forall f b. (Typeable f, Typeable b) => (forall x. f x -> f x) -> (b -> b)
mkT1 h =
case typeRep #b of
App g y ->
case eqTypeRep g (typeRep #f) of
Just HRefl -> h
_ -> id
_ -> id
mkQ1 :: forall f b q. (Typeable f, Typeable b) => (forall x. f x -> q) -> (b -> q) -> (b -> q)
mkQ1 h =
case typeRep #b of
App g y ->
case eqTypeRep g (typeRep #f) of
Just HRefl -> const h
_ -> id
_ -> id
f :: Maybe x -> String
f _ = "matches"
main :: IO ()
main = do
print (mkQ1 f (\_ -> "doesn't match") (Just 3 :: Maybe Int)) -- matches
print (mkQ1 f (\_ -> "doesn't match") (3 :: Int)) -- doesn't match

Why does this function using a typeclass with overlapping instances behave differently in GHCi?

Background
I have written the following code in Haskell (GHC 8.6.3):
{-# LANGUAGE
NoImplicitPrelude,
MultiParamTypeClasses,
FlexibleInstances, FlexibleContexts,
TypeFamilies, UndecidableInstances,
AllowAmbiguousTypes
#-}
import Prelude(Char, Show, show, undefined, id)
data Nil
nil :: Nil
nil = undefined
instance Show Nil where
show _ = "nil"
data Cons x xs = Cons x xs
deriving Show
class FPack f r where
fpack :: f -> r
instance {-# OVERLAPPABLE #-} f ~ (Nil -> r) => FPack f r where
fpack f = f nil
instance (FPack (x -> b) r, f ~ (Cons a x -> b)) => FPack f (a -> r) where
fpack f a = fpack (\x -> f (Cons a x))
The idea behind this code is to produce a function of variable arity that takes its arguments and packs them into a heterogeneous list.
For example, the following
fpack id "a" "b" :: Cons [Char] (Cons [Char] Nil)
produces the list Cons "a" (Cons "b" nil).
Problem
In general, I want to call fpack by passing id as its f parameter (like above) so I wish to define the following function as a shorthand:
pack = fpack id
If I load the above program into GHCi and execute the above line, pack is defined as desired and its type (as given by :t) is FPack (a -> a) r => r.
So I defined the function like so in my program:
pack :: FPack (a -> a) r => r
pack = fpack id
But this gives the following error when loading said program into GHCi:
bugs\so-pack.hs:31:8: error:
* Overlapping instances for FPack (a0 -> a0) r
arising from a use of `fpack'
Matching givens (or their superclasses):
FPack (a -> a) r
bound by the type signature for:
pack :: forall a r. FPack (a -> a) r => r
at bugs\so-pack.hs:30:1-29
Matching instances:
instance [overlappable] (f ~ (Nil -> r)) => FPack f r
-- Defined at bugs\so-pack.hs:24:31
instance (FPack (x -> b) r, f ~ (Cons a x -> b)) =>
FPack f (a -> r)
-- Defined at bugs\so-pack.hs:27:10
(The choice depends on the instantiation of `a0, r')
* In the expression: fpack id
In an equation for `pack': pack = fpack id
|
31 | pack = fpack id
|
This leads me to my questions. Why does this function work when defined in GHCi, but not when defined in the program proper? Is there a way I can make this work in the program proper? If so, how?
My Thoughts
From what I understand about GHC and Haskell, this error comes from the fact that pack could resolve to either one of the two overlapping instances, and that bothers GHC. However, I thought that the AllowAmbiguousTypes option should solve that problem by deferring instance selection to the final call site. Unfortunately, that apparently that isn't sufficient. I am curious as to why, but I am even more curious as to why GHCi accepts this definition in its REPL loop but it does not accept it when it is inside a program.
A Tangent
I have another question regarding this program that is not directly related to main thrust of this question, but I thought it might be wise to ask it here rather than create another question about the same program.
As seen in the example above i.e.
fpack id "a" "b" :: Cons [Char] (Cons [Char] Nil)
I have to provide an explicit type signature to fpack in order for it to work as desired. If I don't provide one (i.e. just call fpack id "a" "b"), GHCi produces the following error:
<interactive>:120:1: error:
* Couldn't match type `Cons [Char] (Cons [Char] Nil)' with `()'
arising from a use of `it'
* In the first argument of `System.IO.print', namely `it'
In a stmt of an interactive GHCi command: System.IO.print it
Is there any way I could alter the definition of fpack to get GHC to infer the proper type signature?
You need to instantiate fpack manually.
pack :: forall a r . FPack (a -> a) r => r
pack = fpack #(a->a) #r id
This requires ScopedTypeVariables, TypeApplications, AllowAmbiguousTypes.
Alternatively, provide a type for id.
pack :: forall a r . FPack (a -> a) r => r
pack = fpack (id :: a -> a)
The issue it that GHC can't see if it should use the fpack provided by that FPack (a->a) r constraint. That might be puzzling at first, but note that fpack (id :: T -> T) could also correctly generate r if there is some instance FPack (T -> T) r available. Since id could be both a -> a and T -> T (for any T), GHC can not safely choose.
One can see this phenomenon in the type error, since GHC mentions a0. That type variable stands for some type which might be a, but might also be something else. One then can try to guess why the code isn't forcing a0 = a, pretending there are other instances around that could be used instead.

Why can't GHC typecheck this function involving polymorphism and existential types?

I have some Haskell code that won't compile (with GHC 8.0.2). I think I understand the basic problem, but I would like to understand it better so I can avoid this in the future.
My library looks similar to this:
{-# language TypeFamilyDependencies #-}
{-# language GADTs #-}
{-# language RankNTypes #-}
module Lib where
type Key = Int
class Handle m where
type Connection m = c | c -> m
withConnection :: Connection m -> m a -> IO a
class (Handle m) => Data m where
getKeyVal :: Key -> m String
data SomeConn where
SomeConn :: (Data m) => Connection m -> SomeConn
useConnection :: SomeConn -> (forall m. Data m => m String) -> IO String
useConnection (SomeConn c) action = withConnection c action
The idea is that Data m represents a class of monads similar to ReaderT (Connection m) IO. I am hoping to write generic functions with the methods of this typeclass, and have the exact method instance be determined by the connection type wrapped with SomeConn (which is chosen at run-time).
Now the following code
getKeyValWith :: SomeConn -> Key -> IO String
getKeyValWith c = (useConnection c). getKeyVal
gives me the following error from GHC 8.0.2:
• Couldn't match type ‘m0 String’
with ‘forall (m :: * -> *). Data m => m String’
Expected type: m0 String -> IO String
Actual type: (forall (m :: * -> *). Data m => m String)
-> IO String
• In the first argument of ‘(.)’, namely ‘useConnection c’
In the expression: useConnection c . getKeyVal
In an equation for ‘getKeyValWith’:
getKeyValWith c = useConnection c . getKeyVal
Strangely enough, the following works just fine:
getKeyValWith c k = useConnection c (getKeyVal k)
Less surprisingly, so does this:
getKeyValWith (SomeConn c) = withConnection c . getKeyVal
Is there a simple rule to understand why GHC doesn't like the first example, but the other examples are okay? Is there a way I can ask GHC for more information about what it's doing when it tries to compile the first definition? I understand this is probably not idiomatic Haskell (what some call the "Existential/typeclass anti-pattern").
Edit:
I should add that I run into the same problem even if I explicitly add the type getKeyVal :: Key -> (Data m => m String) in the first example. I can even give this function its own name with my chosen type signature (which typechecks), but I get the same error. But I see now that even when I explicitly add the type, running :t in GHCI (with -XRankNTypes) on it gives me back the original type with Data m => floated to the left. So I think I understand why GHC is balking at me. Can I force GHC to use my chosen type?
This is all about .. It's unable to pass a polymorphic argument between the functions, hence f . g doesn't work if f is rank-2 polymorphic. Notice the following works:
(~.) :: ((∀ m. Data m => m String) -> z) -> (x -> (∀ m. Data m => m String))
-> x -> z
(~.) f g x = f (g x)
getKeyValWith :: SomeConn -> Key -> IO String
getKeyValWith c = useConnection c ~. getKeyVal
Ideally, . would have a type like
(.) :: ∀ c . ((∀ y . c y => y) -> z) -> x -> ((∀ y . c y => y) -> x)
-> x -> z
and thus cover all special cases like ~. above. But this is not possible – it would require inferring the weakest possible constraint c to pick in any given situation – in the traditional case, c y = y~y₀ – and I'm pretty sure that is uncomputable in general.
(An interesting question is how far we could get if the compiler just inlined . as much as possible before type-checking, as it right now already does with $. If it did automatic eta-expansion, it could certainly get useConnection c . getKeyVal to work, but automatic eta-expansion is in general not a good idea...)
Hiding the Rank-2 polymorphism by wrapping the polymorphic argument in a GADT, as you did with SomeConn, is the usual workaround.

Typeclasses 101: GHC too "eager" to derive instance?

Given the following code:
class C a where
foo :: a -> a
f :: (C a) => a -> a
f = id
p :: (C a) => (a -> a) -> a -> a
p g = foo . g
Now, if I try to invoke p f, GHC complains:
> p f
No instance for (C a0) arising from a use of `p'
In the expression: p f
In an equation for `it': it = p f
I find that somewhat surprising, since f only accepts an "a" which has to be an instance of the typeclass C. What is the reason?
Edit: I know I did not define any instance for C but shouldn't the "proper" response be:
p f :: (C a) => a -> a
When you put a plain expression into ghci, it is basically trying to print it, so
> p f
is approximately the same as having the following in a file
main :: IO ()
main = print $ p f
As you pointed out, p f :: (C a) => a -> a. In order to print $ p f GHC needs to evaluate p f. GHC cannot evaluate a value with a type class context without choosing a dictionary to pass in. To do so, it needs to find a C a instance for all a, which doesn't exit. It also needs to find a Show instance for a -> a. The inability to find either of these results in two errors
No instance for (Show (a -> a)) arising from a use of `print'
No instance for (C a) arising from a use of `p'
It's the dreaded monomorphism restriction in action. By default, GHC doesn't allow us to have top-level value definitions without type annotations if the inferred type has a class constraint. You can remedy the situation by
a.) Turning the restriction off by adding {-# LANGUAGE NoMonomorphismRestriction #-} to the top of your source.
b.) Adding type annotations to affected top-level bindings:
foo :: C a => a -> a
foo = p f
c.) Expanding function definitions (if possible):
foo x = p f x

Typed expression parser

I'm trying to create a typed expression parser in Haskell, which works great so far, but I'm currently struggling to implement higher order functions. I've boiled the problem down to a simple example:
{-# LANGUAGE TypeFamilies,GADTs,FlexibleContexts,RankNTypes #-}
-- A function has an argument type and a result type
class Fun f where
type FunArg f
type FunRes f
-- Expressions are either constants of function applications
data Expr a where
Const :: a -> Expr a
App :: Fun f => f -> FunArg f -> Expr (FunRes f)
-- A very simple function
data Plus = Plus
-- Which takes two integer expressions and returns an integer expression
instance Fun Plus where
type FunArg Plus = (Expr Int,Expr Int)
type FunRes Plus = Int
-- A more complicated function which lifts a function to lists (like in haskell)
data Map f r = Map f
-- For this we need the concept of lifting function arguments:
class Liftable a where
type LiftRes a
-- A singleton argument is lifted by changing the expression type from a to [a]
instance Liftable (Expr a) where
type LiftRes (Expr a) = Expr [a]
-- Two function arguments are lifted by lifting each argument
instance (Liftable a,Liftable b) => Liftable (a,b) where
type LiftRes (a,b) = (LiftRes a,LiftRes b)
-- Now we can declare a function instance for Map
instance (Fun f,Liftable (FunArg f),r ~ LiftRes (FunArg f)) => Fun (Map f r) where
type FunArg (Map f r) = r
type FunRes (Map f r) = [FunRes f]
-- Now a parser for functions:
parseFun :: [String] -> (forall f. Fun f => f -> a) -> a
-- The parser for the plus function is easy:
parseFun ["plus"] f = f Plus
-- But the parser for map is not possible:
parseFun ("map":sym) f
= parseFun sym (\fun -> f (Map fun))
The problem seems to be that there is no way to convince the type checker that every LiftRes is itself Liftable, because recursive class declarations are forbidden.
My question is: How do I make this work? Are there other examples of typed expression parsers from which I could take hints?
EDIT: It seems that this discussion about type family constraints seems to be very related. However, I fail to make their solution work in my case, maybe someone can help with that?
The easiest way to make your example work is to remove the Liftable (FunArg f) constraint from the instance declaration. But I think your example is just so condensed that it doesn't show why you actually need it.
So the next best thing is to add a Liftable (FunArg f) superclass constraint to the Fun class:
class Liftable (FunArg f) => Fun f where
...
If this is not feasible (i.e., if not all your functions have liftable argument types), then you cannot expect to write a parseFun of the given type.
A more general remark: I think what you're trying to do here is very strange, and perhaps too much at once. Parsing from unstructured strings into a context-free datatype is already difficult enough. Why not do that first, and write a separate function that transforms the "untyped", but structured representation of your language into a typed one.
EDIT (as a reaction to the comments, revised): As pointed out in the discussion on type family constraints that you also linked in your question, you can bypass the superclass cycle restriction by using ConstraintKinds. Here is a way to make your reduced example work. Perhaps this will scale to the full solution?
{-# LANGUAGE RankNTypes, ScopedTypeVariables, TypeFamilies, FlexibleContexts, GADTs #-}
import Data.Constraint -- from the constraints package
import Data.Proxy -- from the tagged package
-- A function has an argument type and a result type
class Liftable (FunArg f) => Fun f where
type FunArg f
type FunRes f
-- Expr, Plus, and instance Fun Plus as before
class Liftable a where
type LiftRes a
get :: p a -> Dict (Liftable (LiftRes a))
-- acquire "superclass" dictionary by calling this method and
-- then pattern matching on the result
instance Liftable (Expr a) where
type LiftRes (Expr a) = Expr [a]
get _ = Dict
instance (Liftable a, Liftable b) => Liftable (a, b) where
type LiftRes (a, b) = (LiftRes a, LiftRes b)
get (_ :: p (a, b)) =
case get (Proxy :: Proxy a) of -- extra code required
Dict -> case get (Proxy :: Proxy b) of -- extra code required
Dict -> Dict
data Map f r = Map f
instance (Fun f, Liftable r, r ~ LiftRes (FunArg f)) => Fun (Map f r) where
type FunArg (Map f r) = r
type FunRes (Map f r) = [FunRes f]
parseFun :: forall a. [String] -> (forall f. Fun f => f -> a) -> a
parseFun ["plus"] f = f Plus
parseFun ("map" : sym) f = parseFun sym
(\ (fun :: g) -> case get (Proxy :: Proxy (FunArg g)) of -- extra code required
Dict -> f (Map fun))

Resources