Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 981
rubystallion Avatar asked Mar 24 '17 12:03

rubystallion


People also ask

What are the types of polymorphism in Java?

Polymorphism in Java 1 Compile-time polymorphism: It is also known as static polymorphism. This type of polymorphism is achieved by function... 2 Runtime polymorphism: It is also known as Dynamic Method Dispatch. It is a process in which a function call to the... More ...

Which is an example of a polymorphic function in Python?

Examples of used defined polymorphic functions : def add (x, y, z = 0): return x + y+z. print(add (2, 3)) print(add (2, 3, 4)) Output: 5 9. Polymorphism with class methods: Below code shows how python can use two different class types, in the same way.

What is polymorphism in C++?

More specifically, it is the ability of a program to process objects differently depending on their data type or class. Compile-time Polymorphism − This type of polymorphism can be achieved using method overloading. Run-time Polymorphism − This type of polymorphism can be achieved using method overriding and virtual functions.

What is polymorphism in functional programming?

Functional Programming - Polymorphism. Polymorphism, in terms of programming, means reusing a single code multiple times. More specifically, it is the ability of a program to process objects differently depending on their data type or class. Compile-time Polymorphism − This type of polymorphism can be achieved using method overloading.


3 Answers

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
like image 93
basile-henry Avatar answered Oct 23 '22 21:10

basile-henry


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.

like image 22
Shersh Avatar answered Oct 23 '22 22:10

Shersh


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

like image 2
jakubdaniel Avatar answered Oct 23 '22 22:10

jakubdaniel