0

Imagine that there is some class:

class Foo a where
  g :: b -> a
  f :: a -> b

I want to be able to write the following function:

h :: a -> a
h = f . g

But to do this I need some way to specify the exact instance of Foo I'm using. Is there some way to do this simillar to how Rust uses traits? I'm pretty certain I could use template haskell to build this function with a supplied class each time, but if possible I'd like to about that.

pricks
  • 341
  • 1
  • 2
  • 12
  • 2
    But `h` has type `a -> a`. – Willem Van Onsem May 01 '18 at 19:25
  • Just fixed that, thanks – pricks May 01 '18 at 19:26
  • 1
    But are `b` the same type in `f` and `g`? They are not a part of the class signature, so they could be anything? – 9000 May 01 '18 at 19:31
  • I dont really care what value b is. I need a way to specifify the value of the class used within h. In this case h could be a -> b or a -> a. I dont think that matters. – pricks May 01 '18 at 19:33
  • 1
    @pricks: well the problem is that - depending on the type of `b` - there can be several "correct" results for `h x` (with `x` a value). Since if `b ~ Int`, then we might attach a different meaning to it, than when it is a `Char`. – Willem Van Onsem May 01 '18 at 19:37
  • 1
    Related: [What is the difference between traits in Rust and typeclasses in Haskell?](https://stackoverflow.com/questions/28123453/what-is-the-difference-between-traits-in-rust-and-typeclasses-in-haskell/29388580#29388580) – AJF May 01 '18 at 19:45

1 Answers1

3

If you want h to decide internally which instance to use, just provide an annotation to remove the ambiguity. Say, if you want to choose b ~ Int

h :: Foo a => a -> a
h = f . (g :: Int -> a)

(I guess Foo will need another parameter b in your actual class, but in such case this approach can be adapted as needed).

If you instead need the caller of h to choose the instance, not h itself, you can use AmbiguousTypes and TypeApplications (and ScopedTypeVariables).

h :: forall b a . Foo a => a -> a
h = f . (g :: b -> a)

Later on, you can call h as in, e.g., h @ Int to choose b ~ Int.

chi
  • 111,837
  • 3
  • 133
  • 218
  • How safe/unsafe are AmbiguaousTypes and TypeApplications? – pricks May 01 '18 at 20:22
  • 1
    @pricks They are perfectly safe, despite their scary name. The only price to pay for ambiguous types is that inference can not guess what `b` is since `b` does not occur in `h`'s type, so you will always need to use `h @ SomeType` in every call. These two extensions are usually used together. – chi May 01 '18 at 20:27
  • Thank you, this was exactly the solution a was looking for. – pricks May 01 '18 at 20:28
  • If you’re using type applications, you can also write `h = f . g @_ @b` (or `g @a @b`). – Antal Spector-Zabusky May 03 '18 at 19:05