Using a string variable as a type literal

Following along this blog post about heterogeneous lists.

In the end there is an example where the author creates a Key Value pair and uses Type application to create an getter function:

extensibleRow :: HList '[KV "item" String, KV "quantity" Int, KV "price" Float, KV "date" String]
extensibleRow = V "Apple" %: V 10 %: V 0.5 %: V "2018/1/1" %: HNil

...

> select @"item" extensibleRow
"Apple"

I tried the following:

> let item = "item"
> select (someSymbolVal "item")

But the compiler complains with:

Couldn't match expected type: HList xs0 with SomeSymbol

Is it possible to do this sort of matching?

someSymbolVal returns a SomeSymbol. select expects an HList xs0, apparently.

What is it you’re trying to accomplish that select @"item" extensibleRow doesn’t do?

If your question is “can I just match arbitrary runtime strings with arbitrary type-level strings”, the answer is no. GHC wipes types during compilation, so there is simply nothing to match against.

You can ask the compiler to store you a type-level string during runtime, in what is known as a term-level witness:

f :: String -> Maybe (Either String Float)
f x =
  case someSymbolVal x of
    SomeSymbol (s :: Proxy n)
      | Just T.Refl <- sameSymbol s (Proxy @"item")  -> Just . Left  $ select @n extensibleRow
      | Just T.Refl <- sameSymbol s (Proxy @"price") -> Just . Right $ select @n extensibleRow
      | otherwise                                    -> Nothing

In practice pattern matching runtime strings against each other is more convenient than inventing wacky type-level trees, so all of this is only of academic interest.


For a future where you actually can pattern match arbitrary runtime stuff against arbitrary type-level stuff see Dependent Haskell.