Can one write an archiveByKey?

I was abstracting out some common code patterns in my choices, and I noticed that for a specific key type, I was calling exerciseByKey on it. When I tried to generalize it, I ran into an ambiguity check in the compiler, which i think boils down to not being able to write an archiveByKey function. Specifically:

{-# LANGUAGE AllowAmbiguousTypes #-}

archiveByKey : HasFetchByKey t k => k -> Update ()
archiveByKey k = exerciseByKey k Archive

does not compile with:

    • Could not deduce (HasFetchByKey t0 k)
      from the context: HasFetchByKey t k
        bound by the type signature for:
                   archiveByKey : forall t k. HasFetchByKey t k => k -> Update ()
        at Foo.daml:6:16-50
      The type variable ‘t0’ is ambiguous
    • In the ambiguity check for ‘archiveByKey’
      To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
      In the type signature:
        archiveByKey : HasFetchByKey t k => k -> Update ()

Do I need another constraint on my function? This

archiveByKey : forall t k. HasFetchByKey t k => k -> Update ()
archiveByKey k = exerciseByKey @t @k k Archive

also does not type check.

2 Likes

The right type is

archiveByKey : forall t k. (HasFetchByKey t k, HasExercise t Archive ()) => k -> Update ()
archiveByKey k = exerciseByKey @t @k k Archive

However, you also need to enable AllowAmbiguousTypes by adding the following header to your file

{-# LANGUAGE AllowAmbiguousTypes #-}

So what is going wrong here?

Exercise by key does not allow the compiler to infer the template type via either the arguments or the return type: The argument is the key type which can be the same for different templates and the choice argument which can also be the same for different choices and the same holds for the same result type.

So that’s why you need the explicit type applications and then you run into the usual issue around ScopedTypeVariables where you have to add the foralls explicitly to make them available in the body.

The need for AllowAmbiguousTypes is actually due to the same reason: The ability to specify type applications explicitly is actually a relatively recent addition in Haskell. Before that, it would have been impossible to use a function where you cannot infer the types via the arguments or the result type (that’s why you sometimes see people add proxy arguments to a function just to specify a type). To avoid writing functions that you then later can not use GHC by default does not allow the definition of such functions but you can allow them if you specify AllowAmbiguousTypes. In a world with type applications this is perfectly fine to do but it’s usually still something that you want to think explicitly and think about whether the users of your API are happy to specify the types explicitly or whether you want to design your API slightly differently.

1 Like

@cocreature I believe you but something odd is going on

And now I know that the pragma belongs above the module declaration… is that intended?

1 Like

Yes, pragmas need to go above all source code including module declarations.

1 Like