https://kotlinlang.org logo
m

marcusfs

11/28/2018, 3:10 PM
i don't know if this has been discussed in the past, but it'd be really nice if there was an enum-style shorthand for sealed data classes. basically so you can do something like this:
Copy code
sealed data class Foo {
    First,
    Second(val x: Int),
    Third(val s: String)
}
instead of this:
Copy code
sealed class Foo {
    object First : Foo()
    data class Second(val x: Int) : Foo()
    data class Third(val s: String) : Foo()
}
it's not a huge difference but it feels unnecessarily messy to do it the current way (especially when you mix in "no argument" data classes and have to use object/class instead) , and I imagine this is a pretty common use-case for sealed classes. what do you think?
👍 3
👎 6
o

orangy

11/28/2018, 3:14 PM
It cannot be
enum
, all enum values are singletons.
m

marcusfs

11/28/2018, 3:20 PM
well yes it can't be a java enum, but it can be inner classes with a hidden constructor (so like sealed classes). or did you mean there is something conceptually in the language, or practical in the compiler that prevents the keyword from being used?
o

orangy

11/28/2018, 3:21 PM
enum is enum 🙂 it has
values
,
valueOf
, etc, that assumes all variants are singletons. Using same word for something that is significantly different is very confusing.
m

marcusfs

11/28/2018, 3:23 PM
fair enough 🙂 either way i think it'd be useful with a shorthand for algebraic data types like this
o

orangy

11/28/2018, 3:23 PM
enum
comes from the fact that all values are
enumerable
, after all.
m

marcusfs

11/28/2018, 3:24 PM
that's true - in this case you would only be able to enumerate the constructors
o

orangy

11/28/2018, 3:24 PM
so what’s wrong with sealed classes as they are?
having to write some
:Foo()
and
data class
?
m

marcusfs

11/28/2018, 3:25 PM
it's not that it's wrong, it just seems like a common use-case is unnecessarily messy
yes, and sometimes
object
if you don't have any arguments to a case
(or
class
i guess)
o

orangy

11/28/2018, 3:25 PM
It may be wordy, but I don’t think it’s messy. It states pretty explicitly what is what
m

marcusfs

11/28/2018, 3:29 PM
i guess it's a matter of taste to some extent, but especially that you have to jump between using object and data class makes the code align really poorly which doesn't help readability
and it can get quite noisy as well. here's a more practical example from an android app:
Copy code
sealed class AddEditTaskEvent {
    data class DefinitionCompleted(val title: String, val description: String) : AddEditTaskEvent()
    object CreatedSuccessfully : AddEditTaskEvent()
    data class CreationFailed(val reason: String) : AddEditTaskEvent()
    object UpdatedSuccessfully : AddEditTaskEvent()
    data class UpdateFailed(val reason: String) : AddEditTaskEvent()
}
versus
Copy code
enum data class AddEditTaskEvent {
    DefinitionCompleted(val title: String, val description: String),
    CreatedSuccessfully,
    CreationFailed(val reason: String),
    UpdatedSuccessfully,
    UpdateFailed(val reason: String)
}
o

orangy

11/28/2018, 3:38 PM
I agree with an idea that it could look better in specific cases. I don’t think
enum
is appropriate shortcut here.
As a side note you’d probably have a kdoc before each of the variants, no? 😉
m

marcusfs

11/28/2018, 3:39 PM
i'm not so attached to the name, it's really the shorthand i care about
it really depends tbh but i hear what you're saying 🙂 with functional patterns types can work quite well as documentation though, and documentation that type-checks has better longevity than comments 😉
k

karelpeeters

11/28/2018, 11:30 PM
(FYI: this exact concept is called
enum
in rust, which misses the JVM concept of enum entirely)
g

gildor

11/29/2018, 3:12 AM
One more problem with this approach, that now you have choice: use nested classes or don’t, for example your code snippet can looks as:
Copy code
sealed class AddEditTaskEvent
data class DefinitionCompleted(val title: String, val description: String) : AddEditTaskEvent()
object CreatedSuccessfully : AddEditTaskEvent()
data class CreationFailed(val reason: String) : AddEditTaskEvent()
object UpdatedSuccessfully : AddEditTaskEvent()
data class UpdateFailed(val reason: String) : AddEditTaskEvent()
And used as:
Copy code
CreationFailed("reason)
instead of
Copy code
AddEditTaskEvent.CreationFailed
But with implicit sealed class you don’t have such choice.
m

marcusfs

11/29/2018, 9:17 AM
well, you can always import them explicitly - this isn't different from how enums work
g

gildor

11/29/2018, 9:20 AM
you can import explicitly, sure, but it’s all about naming. By default will be used full name, because sometimes it makes a lot of sense for naming. Like
Event.Success
vs
SuccessEvent
, you don’t want to have
Event.SuccessEvent
, I just pointed that this proposal doesn’t work well with non-nested classes
m

marcusfs

11/29/2018, 9:20 AM
btw for the record swifts
enum
(or enum-with-associated-data as they refer to it) works like rust too. one really frustrating thing about their enum though is that the cases aren't subtypes of the enum type - in fact they are not even types at all and you can only access them using pattern matching.
@gildor ah, yes that's true. i don't think this would be useful for every use-case though, just the case where you are using sealed data classes as an algebraic data type. for example the equivalent in other languages would be: Swift
Copy code
enum Foo {
    case First
    case Second(Int)
    case Third(String)
}
Rust
Copy code
enum Foo {
    First,
    Second(int),
    Third(String)
}
Haskell
Copy code
data Foo = First | Second Int | Third String
sealed data classes already have the right semantics for this, it's just that i think the syntax when declaring it is a bit verbose, as i stated above
g

gildor

11/29/2018, 9:35 AM
See, version of Haskell is exactly what I’m talking about. It’s
First
, not
Foo.First
. i don’t think this would be useful for every use-case
That’s the problem, you suggest new language feature and new syntax, which is big change itself, so should work for so many cases as it possible Maybe there are some other ways to avoid additional verbosity, but now it’s pretty limited, for example you also want to have constructor properties for parent sealed class, but this syntax doesn’t allow that
m

marcusfs

11/29/2018, 9:44 AM
honestly i would like this to be more of an extension/shorthand to existing language features rather than a completely new one. isn't there already precedent where language features work as shorthand for other features but don't support every possible scenario? i'm thinking of things like interface delegation being shorthand for implementing interfaces yourself (delegation is less verbose but also a strictly less powerful way to do it)
g

gildor

11/29/2018, 9:46 AM
class delegation is definitely a bad example of well designed feature and Kotlin team and Andrey Breslav many times mentioned this
m

marcusfs

11/29/2018, 9:47 AM
fair enough, it was just the first thing that came to mind
g

gildor

11/29/2018, 9:47 AM
but even in case of delegation it’s potentianlly removes much more boilerplate than this proposal (which is very limited)
m

marcusfs

11/29/2018, 9:48 AM
but do you think the idea of having a shorthand for this is a fundamentally flawed idea for kotlin, or is it more about my specific proposal?
g

gildor

11/29/2018, 9:49 AM
I would like to write less code for sealed classes, but I don’t have any good solutions which are flexible enough and play nicely with existing code.
and I think proposed solution is too limited to be a part of the language: enum keyword is misused, what about not nested classes, how to share properties
if use case is only for simplest cases (like Foo above), than I don’t see big advantages, it actually creates a new syntax for one particular use case of existing feature
m

marcusfs

11/29/2018, 9:57 AM
if it is a common use-case i don't see why it would be a bad idea to provide shorthand? just like
data class
is shorthand for a specific kind of
class
(namely a product type), i'd really like to see something like a
sealed data class
as a complement for supporting sum types.
g

gildor

11/29/2018, 10:00 AM
because data class saves a lot of typing
this one saves one constructor call without arguments
also has completely new semantics about classes, data classes and object
m

marcusfs

11/29/2018, 10:01 AM
plus mixed `object`/`data class` prefixes
g

gildor

11/29/2018, 10:01 AM
yes, and this is what I mean about new semantics, sorry bad word for this probably
what if you need just a class?
, i’d really like to see something like a
sealed data class
this actually interesting addition, maybe this makes more sense, because doesn’t clash with existing implementation
m

marcusfs

11/29/2018, 10:06 AM
that is the concept i'm trying to describe 🙂
it was in my original message but i updated the code now to also reflect it
sorry about that
o

orangy

11/29/2018, 10:08 AM
sealed data class
clashes with existing meaning. E.g. if you have
inner class
and you make it
inner data class
it doesn’t change syntax that radically
(again, I’m not against the idea itself, I just discard some syntactic ideas based on experience in this language design)
👍 2
m

marcusfs

11/29/2018, 10:09 AM
i really appreciate the feedback!
o

orangy

11/29/2018, 10:13 AM
Also note, that since each item is still a class, it can have a body (with functions, overrides, etc). So syntax should either accomodate for it (and make it non-ambiguous to parse), or disallow it in the short form.
m

marcusfs

11/29/2018, 10:13 AM
yes, that is true
even enums allow bodies after all
g

gildor

11/29/2018, 10:24 AM
even enums allow bodies after all
But the same body for each item, which may be too restrictive for sealed class (because each item has own types in constructor, so probably want to have own methods)
m

marcusfs

11/29/2018, 10:25 AM
yes, i agree
d

Dico

11/29/2018, 10:26 AM
So the thing he's proposing is enum-like syntax for a concept dubbed
sealed data class
. Body of the parent class can follow the
;
just like in enum class (even if I'm not personally a fan of that syntax)
The only thing that remains is how the parent constructor is called - in
enum class
you call the parent constructor instead of declaring a new one
Very interesting idea but it definitely needs to be approached critically. Come up with a design that has answers to points of critique.
m

marcusfs

11/29/2018, 10:35 AM
absolutely - this kind of feedback is precisely what i was looking for when bringing it up
l

louiscad

11/29/2018, 9:20 PM
Enum cases can have different classes as each case can have a body, so they're not so different from one level hierarchy nested sealed classes.
g

gildor

11/30/2018, 1:05 AM
Ah, yes, sure
2 Views