Add makeInstances? #51

Closed
ekmett opened this Issue Sep 19, 2012 · 14 comments

Projects

None yet

2 participants

@ekmett
Owner
ekmett commented Sep 19, 2012

See @orenbenkiki's comment in issue #49

Yes, this is a separate issue and using makeInstances is a better approach. I assume reify would be kind enough to provide a list of instances for the field types for locating the "HasType" ones... and, of course, having makeInstancesFor for explicit decision of which fields to/not create instances for would be needed.

Owner
ekmett commented Sep 19, 2012

One issue with this is it means that the ordering in the source file becomes rather significant as to which instances get silently created. I'm not sure how I feel about that.

@ekmett ekmett was assigned Sep 19, 2012

I don't see instances getting "silently created", makeInstances seems pretty explicit, right? And the programmer has some freedom about where he puts code relative to makeInstances (or makeLenses for that matter). At any rate, this is inherent to Template Haskell and nothing to do with lenses as of themselves...

This is something I never "got" about Template Haskell. On the one hand, it imposes an order between "before the splice point" and "after the splice point". At the same time, it is impossible to use inside $( ) anything that appears "before the splice point" in the same module. It isn't clear why we have both constraints. If compilation is done piecemeal, one would expect to be able to refer to the already-compiled code "before the splice point". If compilation is done in one pass, one would expect order not to matter at all. I suppose the story is far more complex, which is why we end up with the worse of both worlds. Sigh. Sometimes Template Haskell makes me wonder whether I should have picked Clojure instead of Haskell for my system ;-)

Owner
ekmett commented Sep 19, 2012

I think with the separate makeInstances call it would be explicit enough.

The trick is actually writing the thing, because it needs the template haskell generation code to do unification and for us to probably solve #49 as an intermediate step.

I agree that #49 needs to be done first, but why do we need unification? It isn't clear to me how one gets the list of instances for a type (but you implied there was a way). Other than that, it is a simple matter of emitting "instance of HasFoo Bar where foo = theLensOfBarWithTheFooValue", right...?

Owner
ekmett commented Sep 19, 2012

The problem is when Bar a b has a field with a type like Foo a, then I need to find HasFoo, figure out the MPTC parameters unify a's and derive

instance HasFoo (Bar a b) a where
  foo = barFoo

I'm not inclined to derive a one-off hack for the unparameterized form if we're just going to have to throw it all away the moment we address #49.

You are right, this would need to be done, and that's another reason why #49 should be done first. That said, the original definition of Bar a b would have an explicit mapping from a b to whatever type parameters Foo takes, as part of the barFoo field, so "all" you'd need to do is apply the same mapping when setting up the instance... admittedly doing all this in TH is a PITA. Maybe reusing exactly the same type variable names may allow some reuse of the AST, but I wouldn't bet on it.

Owner
ekmett commented Sep 19, 2012

We already do something like this when generating the types of lenses in the first place. We figure out what types from the container are referenced in the target fields, then freshen the remainder to get things like

data Foo a b c d = Foo { _foo :: a, _bar :: b, _baz :: (b,c) }
makeLenses ''Foo

foo :: Lens (Foo a b c d) (Foo e b c f) a e
bar :: Lens (Foo a b c d) (Foo a b c e) b b
baz :: Lens (Foo a b c d) (Foo a b e f) (b,c) (b,e)

A large part of the alchemy is taken care of the alpha substitution traversals I put in Control.Lens.TH.Traversal.

This is actually an easier problem than a bigger outstanding problem we have which is to do unification properly for unifying field types for traversals. (Right now we assume the types all match the type of the first field traversed).

If foo is an a e, shouldn't bar be a b e and baz be (e f) (with the appropriate additional modifications for consistency, of course)?

Owner
ekmett commented Sep 19, 2012

I wasn't quite able to parse that.

To explain what I meant:

All of them let d vary, because they don't use it.

foo's target is completely unconstrained.

bar's b is also used by baz so a local edit to bar, one that ignores baz, can't change the type of that field.

otoh, baz is only partially constrained by the existence of bar, so its not allowed to change its fst (b) type, but the type of the second part of the pair is still up for grabs.

Oh, I see, it is due to being bound by the existence of other fields. Quite right, and makes me even more appreciative of the the fact you didn't take the easy way out by just keeping the types the same, and the complexities of getting all this package right...

Owner
ekmett commented Sep 19, 2012

It gets even more annoying when there are multiple constructors. ;)

Yikes, I never even considered that. That would get very hairy indeed, especially with makeClassy...

Owner
ekmett commented Nov 26, 2012

This issue has been dead in the water for a long time.

I'm closing this out as a 'wontfix' issue, mostly because it isn't clear that it can be done, and there are the host of problems with this approach raised above.

@ekmett ekmett closed this Nov 26, 2012
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment