Skip to content

Add docs for PartiallyAppliedSynonym#181

Merged
chexxor merged 5 commits into
masterfrom
chexxor-partially-applied-synonym
Jun 12, 2018
Merged

Add docs for PartiallyAppliedSynonym#181
chexxor merged 5 commits into
masterfrom
chexxor-partially-applied-synonym

Conversation

@chexxor
Copy link
Copy Markdown
Collaborator

@chexxor chexxor commented May 18, 2018

Type synonyms can be tricky to compose with data types, and likewise it's tricky to understand this type error. Here's a first draft for it.

Comment thread errors/PartiallyAppliedSynonym.md Outdated
## Cause

Explain why a user might see this error.
A type alias has been replaced with its value, and the result is that an expression has been partially applied.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't right really, it's specifically to do with the synonym and the synonym alone:

data Const a b = Const a
data IdentityF f a = IdentityF (f a)

ok :: IdentityF (Const Int) Unit
ok = IdentityF (Const 5)

type A = Const

alsoOk :: IdentityF (A Int) Unit
alsoOk = IdentityF (Const 5)

type B a = Const a

alsoAlsoOk :: IdentityF (B Int) Unit
alsoAlsoOk = IdentityF (Const 5)

type X a b = Const a b

error :: IdentityF (X Int) Unit
error = IdentityF (Const 5)

Allowing partially applied type synonyms basically turns them into type level functions (an example here: https://stackoverflow.com/a/4923883), which sounds cool, but then it's not possible to check their equality in general.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You say that this error is related to the type synonym alone.

So this error has nothing to do with how it's replaced with its underlying type? But it's related to the underlying type somehow, too, no?

If the desugaring pass sees IdentityF (X Int) Unit, how would it know if the type synonym is partially applied? It would need to look at its underlying type, no?

Type synonyms seem benign and great... until you start looking at how to explain their interplay with data types. The problem seems to be a conflict with the syntax (what it appears to be) and the semantics (what it means). It looks like it can be used to do whatever type trickery you'd like, like you can with data types, but it's actually a trap.

To make a light metaphor, data : Pikachu :: type : Mimikyu: https://www.mymbuzz.com/wp-content/uploads/sites/2/2018/02/Detective-Pikachu-3DS-with-Mimikyu.jpg
It's a water-type! Mimikyu, electric attack! What? You can't do electric attacks? :)

I find this error to be absolutely unexplainable, which is so terribly frustrating. :(

I've looked at this error long enough and decided that it's unexplainable, sadly. The best docs I can come up with is to just not try to explain it and just give a code example of why it happens, and how to fix it.

I'll update.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this error has nothing to do with how it's replaced with its underlying type? But it's related to the underlying type somehow, too, no?

No, take a look at the example - A, B, and X are all synonyms for exactly the same thing, they only differ in their defined arity, I did that to illustrate the issue is not to do with the result after expanding the synonym, but the way the synonym itself is used.

You can't explain this restriction in terms of some other part of the type system, because it's specific to synonyms. Synonyms need to exist with their own treatment in the type system as if they were somehow an alias for existing constructions you wouldn't be able to say things like type X a = a. newtype and data can be talked about interchangeably because newtype is just a restriction of data with guarantees about its runtime representation, there are no additional capabilities.

There are some good arguments for not having synonyms at all, but there are also some extremely tedious consequences of that approach. 😄

The why of the restriction is what I mentioned about them becoming functions if partial application is allowed, so having undecidable equality in general. Which makes typechecking undecidable (although there are probably ways that can happen now via fundeps, etc. but not with types alone).

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@garyb Oh! I see it now! I stared at your code snippet for ~20 minutes without getting any meaning from it. Your persistence wasn't for naught. I tried to explain it in my latest commit here.

In my explanation, there is still a thing which seems inconsistent. I'm fine with it, but I'm sure such an inconsistency will frustrate others.

To properly use a type alias having type parameters, you must apply it to a type parameter at every location in the program...

and

A type synonym can be partially applied in the context of a type synonym, however.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A type synonym can be partially applied in the context of a type synonym, however.

Hmm, interesting! That's probably not intentional.

Comment thread errors/PartiallyAppliedSynonym.md Outdated
## Fix

- Suggest possible solutions.
Reconsider how you're using type aliases and data types. Using a type aliase as if it is a normal data type may cause problems, as it not their intended purpose.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aliase -> alias

@chexxor
Copy link
Copy Markdown
Collaborator Author

chexxor commented May 19, 2018

This is ready for a review again. Thanks so much @garyb !

@garyb
Copy link
Copy Markdown
Member

garyb commented May 21, 2018

Might also be worth mentioning that when defining synonyms they should be defined as un-applied as possible, so they can be used in the widest range of situations (for example, the A/B/X types, the A version would be the most preferable definition).

(There's an issue to add a warning when defining synonyms like this so that the linter would suggest it anyway, but since we don't have it yet...)

@chexxor
Copy link
Copy Markdown
Collaborator Author

chexxor commented May 21, 2018

I found this relatively long discussion on type synonyms: purescript/purescript#2932
Lots of viewpoints discussed. I link from here for other people who come here and want to see more discussion on the topic, though not exactly related to this error.

@chexxor
Copy link
Copy Markdown
Collaborator Author

chexxor commented May 21, 2018

@garyb I read through the type synonym discussions I could find in the purescript/purescript project, but I can't find anything which suggests type A = Const is better than type B a = Const a. Do you have a link to it?

I'll put that suggestion in here, but I need a better understanding of the situation. An existing discussion/related proposal would help me a lot here.

@garyb
Copy link
Copy Markdown
Member

garyb commented May 22, 2018

There's not really much discussion about it because it's "obviously" worse when synonyms can be eta-reduced. Eta-expanding makes it impossible to use the synonym in some situations in which it would otherwise work, and unlike some other restrictions we impose on ourselves using the type system, this one never is never useful - it only prevents you doing things you might want to do, not things you might want to avoid.

An example, from transformers, if State was defined as:

-- note the `a`, in the library this is omitted on both sides
type State s a = StateT s Identity a

it would now be impossible to use State in some situations. When stacking transformers, only the top of the stack has its a provided:

newtype App1 a = App1 (ExceptT MyError (State MyState) a)
newtype App2 a = App2 (WriterT MyLog (ExceptT MyError (State MyState)) a)

as the lower layers are used to fill in a type variable of kind Type -> Type. So in both the above cases, we're only applying MyState to State, meaning it is partially applied as we didn't include a value for the a in the synonym.

The issue for adding the warning is purescript/purescript#2691

@chexxor
Copy link
Copy Markdown
Collaborator Author

chexxor commented Jun 8, 2018

Thanks @garyb! That's a great explanation! I've edited it a bit, to improve a reader's flow, and added it to the "notes" section in the error message.

It might be nice to include this to a section in "type synonym"s documentation.

@chexxor chexxor merged commit 2ae4723 into master Jun 12, 2018
@chexxor chexxor deleted the chexxor-partially-applied-synonym branch June 12, 2018 18:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants