1

So, I'd like to have a function / value like this :

converter :: Converter a

where Converter would be some type family that always results in String and is used just so that we can apply the type that we want to convert to String by using type application e.g.

converter @Int => "Int"

I was thinking of using Typeable class which has typeOf function. Unfortunately that requires an actual value for it to work.

I could use Proxy to supply "actual" value, but than I'd have to remove Proxy from the resulting String and that feels a bit dirty and error prone.

Thoughts?

Reygoch
  • 1,204
  • 1
  • 11
  • 24

2 Answers2

4

We could exploit a bunch of extensions: AllowAmbiguousTypes, TypeApplications, ScopedTypeVariables, and produce a variant of @Cubic's answer.

> :{
| convert :: forall a . Typeable a => String
| convert = show . typeRep $ Proxy @ a
| :}
> convert @ Int
"Int"
> convert @ String
"[Char]"
> convert @ Float
"Float"
chi
  • 111,837
  • 3
  • 133
  • 218
3

Why not just

{-# LANGUAGE TypeApplications #-}

import Data.Typeable

main = print $ typeRep (Proxy @Int)

typeRep is designed to take a proxy and will only concern itself with the type on that proxy (so no need to remove anything). Sure, the additional Proxy in there is not quite the syntax you're looking for but it's pretty much the same thing right?

Cubic
  • 14,902
  • 5
  • 47
  • 92
  • I missed `typeRep` function :D, but still I want something with type signature of `converter :: Typeable => Convert a' that i can just call with simple type application. – Reygoch May 12 '18 at 19:56
  • @Reygoch I'm like 50% sure that's impossible with haskells type system. – Cubic May 12 '18 at 19:57
  • @Reygoch If it wasn't, there would be no need to have things like `Proxy` around in the first place. – Cubic May 12 '18 at 19:58
  • @Cubic Proxies can now be replaced with ambiguous types and explicit type applications. There is almost no reason to use them (IMO), except when dealing with old libraries which predate the new alternative. – chi May 12 '18 at 20:09
  • 1
    @chi The only use I have ever encountered was: `data Some con = forall a. con a => Some (Proxy a)`. Without the `Proxy`, I don’t think a pattern match can bind the type "contained" in a `Some` to a name: `case _ :: Some Show of Some (Proxy :: Proxy introduced) -> _` – HTNW May 12 '18 at 23:02
  • @HTNW Interesting, thanks. There's also [this example](https://stackoverflow.com/a/50160423/3234959) which apparently can't be done without proxies, but I consider that to be a GHC deficiency. – chi May 12 '18 at 23:07
  • @HTNW Without the proxy, this compiles, though `foo :: (forall a. (Show a => b)) -> Some Show -> b ; foo f Some = f` and one can pass an argument like `bar :: forall a. Show a => Int ; bar = length (show (undefined :: a))` which has access to the existentially-quantified `a`. A bit cumbersome, but it seems feasible. – chi May 12 '18 at 23:21