purescript icon indicating copy to clipboard operation
purescript copied to clipboard

Rename Symbol to String

Open kcsongor opened this issue 8 years ago • 20 comments

Symbols behave exactly like Strings at the type-level. Imagining a future where PureScript has datatype-promotion with potentially function promotion as well (much like the singletons library for Haskell), the fact that these are called differently will likely cause issues, or at least confusion.

What this means in Haskell today, is that data-types that use either Symbol or String can only be used at their respected levels. In other words, Symbols can only be promoted, and Strings must be values. The following snippet demonstrates the issue:

{-# LANGUAGE DataKinds      #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE PolyKinds      #-}

module SymbolString where

import GHC.TypeLits

data D1 = D1 String

data D2 = D2 Symbol

data Proxy (a :: k) = Proxy

d1 :: D1
d1 = D1 "foo"


-- Doesn't work:
--d1' :: Proxy ('D1 "foo")
--d1' = Proxy
--
--    • Expected kind ‘String’, but ‘"foo"’ has kind ‘Symbol’

d2 :: Proxy ('D2 "foo")
d2 = Proxy

'D1 has no type-level inhabitants, and D2 has no value-level inhabitants. singletons solves this issue by special-casing String, but I think that's not very elegant.

If datatype-promotion is a planned feature, then I think renaming Symbol to String would make sense sooner rather than later.

Because this would break a lot of existing code, maybe some temporary kind synonym can be introduced that throws a warning?

kcsongor avatar Oct 02 '17 19:10 kcsongor

I don't have any plans to implement datatype promotion right now. The reflection library gives me the basic bits and pieces that I need to move things back and forth to the type level.

I'd rather not rename things just for the sake of the name, so I think we'd need a good reason. Without another reason, I'd rather wait until we decide to implement promotion.

paf31 avatar Oct 02 '17 19:10 paf31

Sure, the only reason I thought of this was that it requires nasty workarounds in Haskell, and it would be better if those two were called the same. Other than promotion, I can't think of any reason why this would make sense.

kcsongor avatar Oct 02 '17 19:10 kcsongor

The risk with waiting (until after 1.0?) is that we'd then be far more locked in to the name Symbol, with more and more people/libraries depending on it.

LiamGoodacre avatar Oct 02 '17 19:10 LiamGoodacre

Also proxies give us the ability to say something like this at least:

class ProxyFor t p | p -> t, t -> p where
  reflect :: p -> t

instance proxyForString :: IsSymbol s => ProxyFor String @s where
  reflect = reflectSymbol

paf31 avatar Oct 02 '17 19:10 paf31

The risk with waiting until after 1.0

Ok, well if we're going to break it, now would be the time.

We'd also have to do something about the name purescript-symbols of course 😄

paf31 avatar Oct 02 '17 19:10 paf31

We could merge that into typelevel-prelude, under Type.Data.String?

LiamGoodacre avatar Oct 02 '17 19:10 LiamGoodacre

👍 for the rename from me. Should we ditch TypeString too, or has that already happened? I seem to remember discussion about it somewhere, but may have just been Slack chatter.

garyb avatar Oct 02 '17 19:10 garyb

The issue with ditching TypeString is that we currently have no alternative. It's used for the Fail type class.

LiamGoodacre avatar Oct 02 '17 19:10 LiamGoodacre

I would rather have TypeString be implemented using a functional dependency.

paf31 avatar Oct 02 '17 19:10 paf31

But it's difficult to know when to render. If we want type expression rendering, I'm not sure a type class is a good approach.

LiamGoodacre avatar Oct 02 '17 19:10 LiamGoodacre

I would rather have TypeString be implemented using a functional dependency.

That would allow these strings to be used in contexts other than Fail - is this the intention? (edit: I don't see why not)

kcsongor avatar Oct 02 '17 19:10 kcsongor

@LiamGoodacre is there something about Symbol that makes it unsuitable for Fail etc.?

garyb avatar Oct 02 '17 19:10 garyb

That would allow these strings to be used in contexts other than Fail - is this the intention?

Yes, they don't get evaluated right now, unless they're in a Fail constraint.

But it's difficult to know when to render.

@LiamGoodacre Do you have an example? I suspect you're right, but I figured they'd get evaluated like anything else at the type level.

paf31 avatar Oct 02 '17 19:10 paf31

I dislike when we overlap names too much, because it creates really tricky error messages to figure out. error something something String... when a beginner plays with a record manipulation library will have them messing with values all over when actually a Label didn't match.

kritzcreek avatar Oct 02 '17 19:10 kritzcreek

If we get DataKinds in the future, what will their syntax be? Will it conflict with these literals? Are prefix apostrophes required, and if so are they also on string literals? Similar questions come to mind.

no-longer-on-githu-b avatar Oct 02 '17 19:10 no-longer-on-githu-b

My point is that the functional dependency doesn't make sense. Lets say you have Render x s but x is actually going to unify with Array Int at some point, but you render it too early so now you just have the string "x". But, that means that Render (Array Int) "x" and Render (Array Int) "Array Int" are both valid? That doesn't meet the meaning of functional dependencies.

LiamGoodacre avatar Oct 02 '17 19:10 LiamGoodacre

Assuming Render x s | x -> s

LiamGoodacre avatar Oct 02 '17 19:10 LiamGoodacre

I think it does make sense. For any of these magic classes, you have to say what instances you would have if you wrote them out manually, and then you can ask if fundeps make sense. In this case, I would generate one instance for every type constructor, delegating to instances for any type arguments. With those instances, I think it makes sense.

Edit:

Lets say you have Render x s

The only way that can be solved is automatically. Otherwise, it should be an overlap anyway.

paf31 avatar Oct 02 '17 19:10 paf31

We're perhaps slightly off topic. But it'd be good to get a prototype impl to play with for this.

LiamGoodacre avatar Oct 02 '17 19:10 LiamGoodacre

I don't think it's worth doing before 1.0 actually. I would prefer it, but it also has its downsides, and what we have now works well enough.

paf31 avatar Oct 02 '17 19:10 paf31