Skip to content

Solving IsSymbol instances#2429

Merged
paf31 merged 4 commits intopurescript:masterfrom
LiamGoodacre:feature/is-symbol-instances
Nov 23, 2016
Merged

Solving IsSymbol instances#2429
paf31 merged 4 commits intopurescript:masterfrom
LiamGoodacre:feature/is-symbol-instances

Conversation

@LiamGoodacre
Copy link
Copy Markdown
Member

Opening this PR for discussion: there are some TODOs and other things.

Currently many of the example tests fail since I updated tests/support/bower.json to specify the current latest versions of libraries. They fail with The foreign module implementation for module Unsafe.Coerce is missing.. I added purescript-unsafe-coerce as a dependency, but it didn't help. So I'm not sure why that fails.

However my SolvingIsSymbol test does pass 👍

= TypeClassDictionaryInScope {
-- | The identifier with which the dictionary can be accessed at runtime
tcdName :: Qualified Ident
tcdName :: n
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Could we just make this into an Expr?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I think I tried something like that previously (think it was Either Expr (Qualified Ident)) but it caused a module dependency cycle. I'll have another look later today.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Makes sense, thanks!

-- ^ Should the solver be allowed to defer errors by skipping constraints?
}

-- TODO: Where would this live?
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think it's fine here. I was planning to move the deriving logic here later too, to get around the type synonym restrictions.

@paf31
Copy link
Copy Markdown
Contributor

paf31 commented Nov 15, 2016

Looks great, thanks!

Yes, you'll have some issues with library versions if you update them all to v2.0 like that. There is an issue to track the update that needs to happen (#2400) but it's probably quite a bit of work. Maybe you can avoid it for now?

@LiamGoodacre
Copy link
Copy Markdown
Member Author

In that case I should probably use purescript-symbols at version 1.0.1 and drop the TypeConcat example.

import Control.Monad.Eff.Console

main = do
log (reflectSymbol (SProxy :: SProxy "literal"))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

How about checking the result too ?

let 
    a = (reflectSymbol (SProxy :: SProxy "literal"))
    b = (reflectSymbol (SProxy :: SProxy (TypeConcat "Hello" "World")))
when (a == "literal" && b == "HelloWorld") (log "Done")

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Sounds good, thanks

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

(also I didn't realise outputting Done was interpretted as 'test passing')

[ ("reflectSymbol", Abs (Left (Ident C.__unused)) (Literal (StringLiteral sym)))
]
classTy = TypeConstructor (fmap coerceProperName C.IsSymbol)
expr = TypedValue False (Literal inst) (TypeApp classTy (TypeLevelString sym))
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I wasn't sure about TypedValue - have I done this correctly? I didn't work out what the boolean argument was for and I didn't see it mentioned in the doc-comment for the constructor.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Do we need TypedValue here? I think it can be removed actually.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

It appeared to be required by exprToCoreFn in src/Language/PureScript/CoreFn/Desugar.hs here:

exprToCoreFn ss com _  (A.TypeClassDictionaryConstructorApp name (A.TypedValue _ (A.Literal (A.ObjectLiteral vs)) _)) =

Otherwise it hits the error case:

exprToCoreFn _ _ _ e =
    error $ "Unexpected value in exprToCoreFn mn: " ++ show e

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Maybe you could add a case for

exprToCoreFn ss com _  (A.TypeClassDictionaryConstructorApp name (A.Literal (A.ObjectLiteral vs))) =

or use a pattern guard here then?

@LiamGoodacre
Copy link
Copy Markdown
Member Author

@paf31: making TypeClassDictionaryInScope store the Expr is going to require some reorganising of module dependencies.

The following is a trimmed down version of our modules and their dependencies:

TypeClassDictionaries

Environment
  TypeClassDictionaries

AST.Declarations
  TypeClassDictionaries
  Environment

AST
  AST.Declarations

Externs
  AST
  Environment
  TypeClassDictionaries

TypeChecker.Monad
  Environment
  TypeClassDictionaries

TypeChecker.Entailment
  AST
  Environment
  TypeChecker.Monad
  TypeClassDictionaries

TypeChecker.Types
  TypeChecker.Entailment

TypeChecker
  AST
  Environment
  TypeChecker.Monad
  TypeChecker.Types
  TypeClassDictionaries

We need TypeClassDictionaries to depend on Expr which is defined in AST.Declarations. This causes a cycle between AST.Declarations, Environment, and TypeClassDictionaries.

@LiamGoodacre
Copy link
Copy Markdown
Member Author

An alternative might be to remove the name/expr from TypeClassDictionaryInScope and just pair the dict up with the Expr where needed. I'll have a think about it.

@LiamGoodacre
Copy link
Copy Markdown
Member Author

Although that is actually the same solution as what I've already done with adding the type parameter.

@paf31
Copy link
Copy Markdown
Contributor

paf31 commented Nov 17, 2016

@LiamGoodacre I think we should stick with the original approach actually, using a sum type, now that I understand the problem properly (sorry!)

But let's rename some things to make them more understandable:

  • tcdName should be named something like tcdValue since it's more general now.
  • NamedTypeClassDictionaryInScope is a bit wordy. How about just NamedDict or something?

, tcdDependencies :: Maybe [Constraint]
}
deriving (Show)
deriving (Show, Functor)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Might as well derive the usual Functor, Foldable and Traversable here.

@paf31
Copy link
Copy Markdown
Contributor

paf31 commented Nov 17, 2016

A third option is a sum type like

data Evidence
  = NamedDictionary Ident
  | IsSymbolDictionary String

right?

@paf31
Copy link
Copy Markdown
Contributor

paf31 commented Nov 17, 2016

I can confirm that this makes my generics-rep PR build successfully.

I did run into one weird issue though. If I don't import Data.Symbol (and IsSymbol specifically) in my test suite, then I get the following error:

psc: An internal error ocurred during compilation: "Missing value in mnLookup"

@paf31
Copy link
Copy Markdown
Contributor

paf31 commented Nov 19, 2016

I think there is relatively little needed to push this over the finish line, right? Just

  • Switching back to a sum type instead of using Expr directly.
  • Dealing with the TypedValue issue.
  • Maybe some renamings here and there (tcdName in particular)

I think once this is merged, we should release 0.10.3, since it will enable some nice library features which depend on generics-rep deriving (among others).

@LiamGoodacre
Copy link
Copy Markdown
Member Author

Hey thanks for the review. I've been away without internet for a few days so I haven't had a chance to work on this. I should be able to get this done today/tomorrow though 👍

@LiamGoodacre
Copy link
Copy Markdown
Member Author

I've been looking into the Missing value in mnLookup issue from the renameModules part of moduleToJs. This occurs because the dictionaries are created inline and use the type class constructor (which hasn't necessarily been imported). The easiest way to fix this is to not use the constructor and just have the object literal - is that okay to do? This also means that I don't need to change exprToCoreFn.

@garyb
Copy link
Copy Markdown
Member

garyb commented Nov 20, 2016

I would say no, not really. The dictionaries are not going to be records when the typechecker gets fixed for #928.

edit: #2205 is a newer version of that actually.

@LiamGoodacre
Copy link
Copy Markdown
Member Author

@garyb yeah that makes sense, thanks. So we'll need to implicitly import the type class constructor during code gen - what would be the preferred way of doing that?

@garyb
Copy link
Copy Markdown
Member

garyb commented Nov 20, 2016

Hmm, that's a tricky one actually... I'm not too sure! Probably as part of the CoreFn desugaring process, if we can figure it out there? There used to be some extra import elaboration done in there, but it was removed after it got figured out propery during name desugaring instead. Pretty sure we won't be able to do it that early on in this case though, right?

fqValues :: A.Expr -> [ModuleName]
fqValues (A.Var q) = getQual' q
fqValues (A.Constructor q) = getQual' q
fqValues (A.TypeClassDictionaryConstructorApp C.IsSymbol _) = getQual' C.IsSymbol
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This lets us implicitly add an import of Data.Symbol if the IsSymbol class is used. The import also gets deduped later if it's already there.
Is this a good place to add this?

Copy link
Copy Markdown
Member Author

@LiamGoodacre LiamGoodacre Nov 20, 2016

Choose a reason for hiding this comment

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

Perhaps it's not great to explicitly mention IsSymbol here: it means we'd have to update multiple places if we're to do similar instance solving in the future. But it's possibly odd to do it for all constructor applications?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Could you please add a comment here explaining why this line is necessary?

import qualified Language.PureScript.Constants as C

type TypeClassDict =
TypeClassDictionaryInScope (Either Expr (Qualified Ident))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think I'd rather use a sum type here, with a constructor for named instances, and one for symbol dictionaries. Then instead of using either id in mkDictionary, you would pattern match, and include the instance generation code there. isSymbolDictionary would just apply the appropriate constructor.

}

-- | Build a type class instance dictionary for the `IsSymbol` class for a symbol
isSymbolDictionary :: String -> TypeClassDict
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I originally read this as a question: "is this a symbol dictionary?" 😄

Maybe something like makeIsSymbolDictionary would be better?

@LiamGoodacre
Copy link
Copy Markdown
Member Author

Currently updating the test example so that it depends on the implicit importing.

@LiamGoodacre
Copy link
Copy Markdown
Member Author

Okay I'm confused: the test is passing on some build jobs and failing on others 😕.

@paf31
Copy link
Copy Markdown
Contributor

paf31 commented Nov 22, 2016

I think you need to update the cabal file.

@paf31 paf31 merged commit 1e6fc9d into purescript:master Nov 23, 2016
@paf31
Copy link
Copy Markdown
Contributor

paf31 commented Nov 23, 2016

Nice work!

@LiamGoodacre LiamGoodacre deleted the feature/is-symbol-instances branch November 23, 2016 08:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants