https://kotlinlang.org logo
n

natpryce

01/15/2021, 1:18 PM
When I use sealed classes, I end up writing lots of functions like:
Copy code
fun doSomethingWith(arg : SealedBaseClass, otherArg: Int) = when(arg) {
    is SubclassA -> doSomethingWith(arg, otherArg)
    is SubclassB -> doSomethingWith(arg, otherArg)
 }

fun doSomethingWith(arg : SubclassA, otherArg: Int) = ... 
fun doSomethingWith(arg : SubclassB, otherArg: Int) = ...
What do people think of the idea of a sealed function, where the compiler generates the when boilerplate for you? E.g.
Copy code
sealed fun doSomethingWith(arg : SealedBaseClass, otherArg: Int)

fun doSomethingWith(arg : SubclassA, otherArg: Int) = ... 
fun doSomethingWith(arg : SubclassB, otherArg: Int) = ...
I’m imagining the same rules would apply as for sealed classes: all implementations of the sealed function have to be in the same source file. And the compiler would check them for exhaustiveness.
2
❤️ 8
s

streetsofboston

01/15/2021, 1:28 PM
You may want to use inheritance instead. Say the
doSomething
was part of class
MyClass
, then you could add an abstract method
abstract fun MyClass.doSomething(): Result
to the SealedBaseClass.
n

natpryce

01/15/2021, 1:30 PM
Polymorphic methods and sealed classes have different use cases. Polymorphic methods are useful when the set of types varies more frequently than the set of operations on those types. Sealed classes are useful when the set of operations varies more frequently than the set of types they apply to. This is a suggestion about sealed classes.
3
l

Lammert Westerhoff

01/15/2021, 2:58 PM
I think
when
gives you more power since you can group multiple classes together and have an else. With such a sealed function that would be difficult. So if you’d have a sealed function like that and later subclasses are added or requirements change you might find yourself changing it to a
when
for the extra functionality. So even though I think it’s a nice idea I think in reality it would come with some potential downsides and it doesn’t really add something you cannot already do.
m

Matiasdelbel

01/15/2021, 3:01 PM
Hey! Maybe this is the wrong thread to ask, but a doubt came to my mind... What do you think about sealed classes and the visitor pattern? Should we replace the use of the visitor pattern with the use of the when clause?
n

natpryce

01/15/2021, 3:16 PM
Personally — Yes. In Kotlin I only use the visitor pattern if a Java library requires it
Visitor methods have to be polymorphic, which means they cannot be inline functions
👍 1
Plus they involve a even more boilerplate than when
n

Nir

01/15/2021, 3:21 PM
Just because you are using sealed classes, doesn't mean you cannot have an interface in the sealed class that all children implement
I don't really see the downside here of having
doSomethingWith
as part of the interface of the sealed class and implementing it in each member; it accomplishes the exact same thing you wrote here, but it avoids almost all the boilerplate, without needing new language features
n

natpryce

01/15/2021, 3:24 PM
Because sealed classes are intended for when operations are implemented separately from the class.
E.g. when the sealed class is in a library and the operations are implemented by users of the library.
n

Nir

01/15/2021, 3:25 PM
okay, fair enough, so you're talking about a case where the person implementing doSomethingWith doesn't own the class
n

natpryce

01/15/2021, 3:25 PM
Sealed classes are a language mechanism for what Java has to do with the Visitor pattern
n

Nir

01/15/2021, 3:26 PM
that's not exactly 1 to 1
the visitor pattern is still open polymorphism
at least, depending on how its done, there's a lot of related things that are called visitor pattern
At any rate though, I think the answer to your question is that there's only really boilerplate here because you've decided to separate out all the functions in this exact way
if you simply inline all the class specific
doSomethingWith
then there's no boilerplate at all
n

natpryce

01/15/2021, 3:28 PM
the
when
boilerplate is still there, now the doSomethingWith is a big function, and you have to do a dispatch on type even when you have a subclass of the sealed class.
n

Nir

01/15/2021, 3:30 PM
it's not boilerplate, whether you write:
Copy code
fun doSomethingWith(arg : SealedBaseClass, otherArg: Int) = when(arg) {
    is SubclassA -> // snippet foo
    is SubclassB -> // snippet bar
 }
Or
Copy code
fun doSomethingWith(arg : SubclassA, otherArg: Int) { // snippet foo }
fun doSomethingWith(arg : SubclassB, otherArg: Int) { // snippet bar}
They are exactly equivalent. In both pieces of code, you mention each subclass once
In the former case you are listing the sub-classes directly to the when, in the latter case you are listing the sub-classes in the types of the function arguments
it's all the same
i see your point though about when you have the subtype
3 Views