3

Let's look at these functions' types for example:

:t traverse
traverse
  :: (Applicative f, Traversable t) => (a -> f b) -> t a -> f (t b)

:t id
id :: a -> a

They have no concrete types, but have generic type parameters: a, f, b, t (correct me if they called not generic type parameters please)

If I combine id and traverse together in this way,

:t traverse id [Just 1, Just 2, Nothing] 
traverse id [Just 1, Just 2, Nothing] :: Num b => Maybe [b]

Haskell can now bind some concrete types for the type variables a, f, b, t.

t = []
a = Maybe bb
f = Maybe
b = Num bb => bb

Below I infer the types and mappings to parameters by hand, is there any way or tool in Haskell to do this automatically, so that it takes some composed parts (id and traverse) in an example, extracts their type signatures in general, and on output produces a mapping from generic type parameters names to concrete inferred types?

See also the first example here: https://wiki.haskell.org/Type_inference for expression "map ord" on how Haskell finds bindings of actual types to names.

So when we look at functions separately we only have names a, f, b, t. But then we combine the functions and provide some extra information like [Just 1, Just 2, Nothing], and the names a, f, b, t are mapped to concrete types.

I want to catch and show this mapping automatically.

6
  • 1
    I’ve voted to reopen this question. I don’t know if you’ll get a satisfying answer, but it’s clear to me what you’re asking: to automatically print out the substitution of type variables that the compiler deduces during typechecking when instantiating the type of a polymorphic definition. I.e., given f :: forall x1…xn. t with type variables x1xn and type t, and an expression containing f, this would print all the values (of kind Type) chosen for x1xn. You may find the information you want in the output of the GHC flags -ddump-tc, -ddump-tc-ast, or -ddump-tc-trace. Commented Dec 12, 2020 at 1:37
  • If I was in error closing it, please correct me. Commented Dec 12, 2020 at 15:17
  • @JonPurdy, I've mistakely voted for reopening this question withouth seeing that your comment is from 13 hours ago while Will Ness closed this question only a few minutes ago, so probably you agree (as I agree) with the dupe that Will marked. Commented Dec 12, 2020 at 15:17
  • @WillNess, I think you're not wrong, even if I'm biased as I asked that question exactly to dupe this (what a pity there's no golden badge for creating a newer question which an older question gets marked as a duplicate of :P). But Jon probably sees in this question another meaning that I don't see. Commented Dec 12, 2020 at 15:19
  • @Enlico: Your question shows how to get the type of a subexpression; in order to answer this question, you take that result and manually unify it with the original polymorphic type to obtain the desired result, a substitution like { fMaybe, t[], aMaybe b0, bb0 }. That works fine for functions, although not so much for more complex subterms. It is certainly a way to get there, just not exactly what I think was asked for—namely, the substitution. Commented Dec 14, 2020 at 19:40

1 Answer 1

-1

I think f and t are more generic type constructor parameters, as they act on a type to give you a type (they kind is * -> *, where * means "a concrete type").

traverse id is not composition, it is function application, as you are passing id as an argument to traverse. this . that is function composition between this and that, in the mathematical sense, where you create a function that gives its (first) argument as an input to that and feeds the result of this application to this.

You refer to the example in this page, where given this

map :: (a -> b) -> [a] -> [b]
Char.ord :: (Char -> Int)

the compiler is able to deduce that the type of map ord is [Char] -> [Int], as you can check yourself by writing :t map ord at the command line.

If you expect a similar output with concerete types when you type :t traverse id, you won't get it, for the simple reason that traverse id is still a polymorphic function, both in its concrete type arguments and type constructor arguments.

Just to give a slightly different example, if you type :t traverse (:[]), where (:[]) has type a -> [a], which is a particular case of the (Applicative f) => a -> f b that traverse expects, you get this output,

traverse (:[]) :: Traversable t => t b -> [t b]

which, compared with :t traverse,

traverse :: (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b)

tells you that traverse, in traverse (:[]), has been "instantiated" with f === [] and a === b.

Sign up to request clarification or add additional context in comments.

5 Comments

I mean not function composition, I mean that I combine two things (functions) together and this combining determines how to correspond names to actual types.
@Evg, that "combine two things" is function application. However, as regards what you want, I still don't get it, sorry (and since the question is closed, at least other two persons agree with me). Maybe you can write in the question an example output that you expect from some wannabe-command or somthing.
Please look at first example here: wiki.haskell.org/Type_inference for expression "map ord" how Haskell find bindings of actual types to names. I also update the question.
@Evg, please give a look at my updated answer. I strongly advice you to edit your question to clarify with code what you mean. The question is still closed, that means whoever is passing by is not willing to reopen it because they don't find it clear, just like me.
@Evg, please also give a look at this question I've just asked. Is that, by any chance, what you were really trying to ask here?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.