[Grace-core] Some comments from Tijs on parsing Grace

James Noble kjx at ecs.vuw.ac.nz
Sun Jul 24 05:24:16 PDT 2016


>> how [does if(_)then(_)else(_)] distinguish between blocks & booleans.  I said something like: "they see if theere's an ifTrueIfFalse method …”
> 
> In minigrace, it’s worse than that.  Not only is if(_)then(_)else(_) compiled inline, but the condition has to be the built-in class boolean, or inherit therefrom.  If they aren’t, then we request them to apply themselves.  We could in principle define if(_)then(_)else(_) in terms of ifTrue(_) and ifFalse(_), but I don’t want to do that because of the performance implications.

yep, so that's something we'll have to see about...
If we had a better version...

> No one has yet filed a bug report saying that their re-implementation of Booleans didn’t work, so this is low on my list of priorities.

:-) 

>> - method parameters should be mentioned in the spec (as "defs", i.e. unassignable). currently they aren’t
> 
> Yes, good catch.  Who should add this?

I threw something in.

>> -- [[ ]] generics are "syntactic vinegar!!" (and the opposite of Gracefulness)
> 
> Yes, indeed.   I think that if your goal at the Grace Workshop was to discourage people from using Grace, you did a good job here.   You could just as easily have used examples like this
> 
> 	max⟦Number⟧(x, y)
> 
> that would have looked good.  If your emacs mode doesn’t yet replace [[ by ⟦ and ]] by ⟧ it should!  

If you really believe that, then remove the table of trigraphs and permit only the unicode versions.  

(BTW I could read the characters on mac mail, not on my ipad - which said I should 
replace [[ and ]] by nothing)

I still hope for a better, ASCII-only, solution.

> As I mentioned to you after the workshop, the way that Emerald dealt with type parameters was to distinguish syntactically (in the method definition) between explicit type parameters and implicit type parameters.  This is from Emerald TR CRL 91-1
> 
> <PastedGraphic-2.pdf>

OK so looking at that, I can see a declaration of the type parameter "t";
and the rule seems to be that t is bound to the static type of the first parameter
to which it appears.   I don't see more syntax than that, either in the "typeobject" or in the "object". 

> So, in the max example, if the declared type of the first argument is T, the second argument is required to 
> conform to T, and the result is guaranteed to conform to T

I can see how that works, but not how explicit and implicit arguments are distinguished.

> — no type arguments necessary.  

no explicit type arguments necessary on the caller side

> You might say that this is insufficiently general, 
> in that the first argument might have more operations than the second, and you would be right: it’s an 80% solution.

it seems like a very simple & straightforward type inference algorithm;
Java's algorithm (and Scala's) is very complicated;
Apparently C#'s algorithm is rather simpler (but is more complex than this I think)

I was keen on some amount of type inference early on,
but I thought we abandoned that idea for a reason: because doing type inference 
meant that types could affect the execution of the program in a non-obvious way.

Michael may remember more, but I think that nasty case is something like:

object { 
   method foo[[T]](a : T, b : T) {
      print "{a}" }
}.foo("hello", 123) 

with the current rule, where types are only "inferred" to Unknown, this code will be accepted and will run, but with type inference that code is a static type error.  

If we're not careful, things get weirder: 

def d = object { 
   method foo[[T]](a : T, b : T) {
      print "{a}" }
}

d.foo("hello", 123) 

Is the type of the def inferred? (originally we said yes, but not for a var; var will be "Unknown" by default).  So 

var v = d
v.foo("hello", 123) 

would not do type inference on the call, because v's type is unknown. 

Things get even worse if we allow T to be used inside the method body - we currently do, C# does, but Java doesn't because of erasure. 

method evil[[T]](x : T , y : T) { 
   match (y) 
     case { z : T -> return "match" }
  return "fail"
}

If we want to support both these cases, it seems hard without making a bigger distinction
between "statically typed" and "dynamically typed" code --- either with a pragma, or a button
on the user interface, or something.   Is that what we want? 

As Jeremy Siek said at the GRACE workshop: gradual typing & type inference fundamentally conflict, because they both offer *different* solutions to filling in omitted type information:
gradual/optional types with "Unknown", type inference with a guess. 

James




More information about the Grace-core mailing list