given ... =is a given alias
also, I find it utterly confusing that there's no
with... similarly for extensions
There's always a keyword for indentation like in Haskell (
while, ...), except for classes where it's
: because Martin thought that worked better, and extension blocks (mostly because they're not really scopes and because you can also write them on one like
extension (x: Int) def foo (y: Int) = ...)
withlike syntax for defs see lampepfl/dotty#10637
type ScopeNode = Comment | Expression | ScopeBlock | ListBlock | EmptyBlock type ListNode = Comment | Element
ListNodeas mixins within the ADT hierarchy
/** * An [[Entity]] that is not allowed to be part of a [[Frag]]. * We use types instead of trait inheritance (at the moment) because we need to * transparently include [[String]]s in the type hierarchy. * @see [[Element]] */ type Modifier = Attr | Style /** * An [[Entity]] that is allowed to be part of a [[Frag]]. * We use types instead of trait inheritance (at the moment) because we need to * transparently include [[String]]s in the type hierarchy. * @see [[Modifier]] */ type Element = Raw | Tag | Frag | String /** * Something [[String]]y (see [[render]]) that represents, in and of itself, a coherent * component of an XML tree. [[TagClass!]]es, [[AttrClass!]]es, and [[StyleClass!]]es do not * make the cut because while they are valid names that may appear in an XML document, they cannot exist in an XML document outside of * their respective entities. We use types instead of trait inheritance (at the moment) because we need to * transparently include [[String]]s in the type hierarchy. * @see [[Modifier]] * @see [[Element]] */ type Entity = Modifier | Element
String, except for
Fragwhich was an
Chain[ListNode]by appending an
Element, it works fine when the resulting
Chain[ListNode]is passed directly into a functionthat takes a
Chain[ListNode], but when the append is done inside a
maplambda and then
mapped with said
Chain[ListNode]-taking function, it can't infer the type parameter on
commentChain.append[ /*Should infer as ListNode*/](element)
ListNodeis a mixin that
Elementboth extend, it infers it just fine
concating lambdas to custom
but also, we shouldn't encourage the (over)use of unions, if you can make a hierarchy that's probably a better idea
I've seen this argument been made before and it kinda feels a bit like a tease to me:
–"Here you go, union types!"
–"Oh cool, I can use them for simplifying stuff.."
–"You shouldn't really use them, they're mostly for the compiler."
My point is I don't understand why, fundamentally, overusing unions could be a bad thing. They look really cool to me, and seem to make a lot of sense in a lot of places. For fun I tried implementing a Result-type where error types are combined with unions instead of widened to a common supertype, and it's awesome! The error domain went from being whatever to actually showing me warnings for unchecked types.
In my mind, a combination of nominal and structural typing—like letting A.x | B.x actually work again—would be greater than the sum of its parts. This has already been explored a tiny bit with explicit nulls and how "flow typing" works within that context.
Yes, @megri @CiaraOBrien @smarter on the (under/over)use of unions, I think the modelling opportunities are great. And I agree that it is better to make a language feature as ergonomic as possible and then instead help with warnings etc if there are some risk of badness. But I would also want to know more about the over-use problems. Myself, when looking at my older code, I see a lot of inheritance stuff that could have been re-modelled as unions. So I perhaps rather have an under-use of them :) I have been playing around with it and find that improved inference would be really helpful if possible. E.g. in the below example, it would be nice to have the compiler figure out enough for me not having to resort to
asInstanceOf in the below model of an
Empty type that can be used when you don't need all the monadic stuff by the heavier, parameterized
Option, e.g. by just
def f(x: Input): Result | Empty = ??? :
sealed abstract class Empty object Empty extends Empty: override def toString = "Empty" extension [T](x: T | Empty) def toOption: Option[T] = x match case Empty => None case _ => Option(x.asInstanceOf[T]) def isEmpty: Boolean = x.isInstanceOf[Empty] def nonEmpty: Boolean = !isEmpty def get: T = x match case Empty => (throw new NoSuchElementException("Empty.get")).asInstanceOf[T] case _ => x.asInstanceOf[T] def getOrElse(default: T): T = x match case Empty => default case _ => x.asInstanceOf[T] end extension
And if you like to do all the monadic coolness with map and flatmap you can just
.toOption on any union with Empty. But perhaps is there already a concise way to get rid of the
asInstanceOf above that I missed?
And it would be nice if the last evaluation below would have been
Int | String
scala> var x: Int | String | Empty = 42 var x: Int | String | Empty = 42 scala> val ie: Int | Empty = 42 val ie: Int | Empty = 42 scala> ie.get val res3: Int = 42 scala> val ise: Int | String | Empty = 42 val ise: Int | String | Empty = 42 scala> ise.get val res4: Matchable = 42
See also: lampepfl/dotty#11449
enumis translated to a sealed abstract class (similar to a sealed trait) under the hood. You can see all the gory details of the translation made by the compiler here: http://dotty.epfl.ch/docs/reference/enums/desugarEnums.html