How can i get the type of a polymorphic function for a specific type class instance?

帅比萌擦擦* 提交于 2019-12-19 18:22:13

问题


For example, typing :t ap in GHCi gives the result

ap :: Monad m => m (a -> b) -> m a -> m b

If I already know the Monad instance I'm going to use is ((->) r), how can I query for the type of ap for that specific instance?


回答1:


You can use visible type application feature to specify parametric types. You can look at functions in more creative way: functions in Haskell can be applied to not only values of some types, but also to types of that values. But to pass type you should somehow specify (with prepending @) that you're passing types (because types are not first-class objects in Haskell yet).

So here how it works:

λ: :set -XTypeApplications
λ: :t ap @((->) Int)
ap @((->) Int) :: (Int -> a -> b) -> (Int -> a) -> Int -> b

The only limitation of such approach is that you can't use type variables in ghci, you should use specific types (Int instead of r) but this is not big deal.

ADVANCED SECTION

Well, actually you can, but it's tricky:

λ: :set -XExplicitForAll 
λ: :set -XPartialTypeSignatures 
λ: :set -XScopedTypeVariables 
λ: :{
λ| foo :: forall r . _
λ| foo = ap @((->) r)
λ| :}
<interactive>:28:19: warning: [-Wpartial-type-signatures]
    • Found type wildcard ‘_’
        standing for ‘(r -> a -> b) -> (r -> a) -> r -> b’
λ: :t foo
foo :: (r -> a -> b) -> (r -> a) -> r -> b

UPD: You can actually use placeholders instead of type variables (see another answer). But if you want to specify exact names use approach from above.

λ: :t ap @((->) _)
ap @((->) _) :: (t -> a -> b) -> (t -> a) -> t -> b

/ADVANCED SECTION

One more thing to say about this approach: you should do something more if your functions have several type parameters and you want to specify exact one. Types are passed one by one from left to right just as simple arguments in some function like bar :: Int -> String -> Double. If you want to fix first argument of bar you should write bar 5 and if you want to fix second, then, well, you can write something like \n -> bar n "baz" but this doesn't work with type application. You need to know two things:

  1. Order of types.
  2. How to specify desired type.

Consider next function:

λ: :t lift
lift :: (Monad m, MonadTrans t) => m a -> t m a

We want be able to specify m and t type variables. Because Haskell has no named type variables (yet) you can't write :t lift {t=MaybeT} or :t lift {m=IO} unfortunately. So go back to two things.

To see order of types you should use some compiler options. Order of type arguments is specified by forall and you can do it manually. Otherwise type parameters will be sorted somehow by the compiler. Mere mortals can't see order of types for lift function but if you're aware of some high-level magic you can:

λ: :set -fprint-explicit-foralls
λ: :t lift
lift
  :: forall {t :: (* -> *) -> * -> *} {a} {m :: * -> *}.
     (Monad m, MonadTrans t) =>
     m a -> t m a

And then you should use @_ to skip some types:

λ: :t lift @MaybeT
lift @MaybeT
  :: forall {a} {m :: * -> *}. Monad m => m a -> MaybeT m a
λ: :t lift @_ @IO
lift @_ @IO
  :: forall {t :: (* -> *) -> * -> *} {a}.
     MonadTrans t =>
     IO a -> t IO a
λ: :t lift @_ @_ @Int
lift @_ @_ @Int
  :: forall {t :: (* -> *) -> * -> *} {t1 :: * -> *}.
     (Monad t1, MonadTrans t) =>
     t1 Int -> t t1 Int

Well, this is really mystery for me why m is shown as third argument in forall but should be passed as second but I'm still not aware of all magic.




回答2:


As Lazersmoke said as a comment you can use the TypeApplications extension that was introduced in GHC 8.0.

In GHCi:

λ > :set -XTypeApplications
λ > import Control.Monad
λ > :t ap @((->) _)
ap @((->) _) :: (t -> a -> b) -> (t -> a) -> t -> b



回答3:


This is just a hack, but you could always do something like:

:t ap . (id :: ((->) r a) -> ((->) r a))

or

:t \x y -> (id :: ...) (ap x y)

interestingly

Prelude Control.Monad> type Reader = (->) r
Prelude Control.Monad> :t ap . (id :: Reader r a -> Reader r a)
ap . (id :: Reader r a -> Reader r a)
  :: Reader r (a -> b) -> (r -> a) -> r -> b

differs from

Prelude Control.Monad> :t \x y -> (id :: Reader r a -> Reader r a) (ap x y)
\x y -> (id :: Reader r a -> Reader r a) (ap x y)
  :: (r -> a1 -> a) -> (r -> a1) -> Reader r a

in what ghc recognizes as the synonym Reader r a



来源:https://stackoverflow.com/questions/42999199/how-can-i-get-the-type-of-a-polymorphic-function-for-a-specific-type-class-insta

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!