3 ms·
Re immutable: If you're doing functional why does this matter? "true" immutable vs. just an immutable view should only matter if the object is retained by the f
by kllrnohj 8y ago
Re immutable: If you're doing functional why does this matter? "true" immutable vs. just an immutable view should only matter if the object is retained by the function it's passed to, but now we're talking object state rather than pure functions.
Re "command-like" objects: I'm not following, can you give an example of something you can do in Scala here that you can't in Kotlin?
- lmm 8y ago> Re immutable: If you're doing functional why does this matter? "true" immutable vs. just an immutable view should only matter if the object is retained by the function it's passed to, but now we're talking object state rather than pure functions. For the same reason that being able to write val rather than var matters. With perfect discipline you can write pure functional code in any language, but in practice (and in codebases that likely have some non-functional sections, particularly if you're interoperating with Java) being able to be 100% sure that a given value is really immutable rather than 99.9% sure makes a huge difference. > Re "command-like" objects: I'm not following, can you give an example of something you can do in Scala here that you can't in Kotlin? There are lots of different use cases for this stuff, but one I was working with today was database transactions. I have operations that should only happen inside a database transaction, and want to enforce that at the type level, but I want to be able to compose several of those functions in a single database transaction, so I don't want to just have each one run its own transaction. So what I do is have a custom type, something like MustHappenInTransaction[A], which is actually just an alias for an existing library type (Free), and I automatically get the ability to use the for/yield syntax to compose these, and can use existing library functions to operate on it almost as if it were a plain value, but still safely (e.g. traverse rather than map on a sequence). I have no way to "get the A out" except for my doTransaction() function (what's why I say it's a command-like object), so the function will definitely happen in a transaction, but I can still do normal function composition, extract out common subfunctions, and all that. You can't do that in Kotlin because there's no equivalent to "do notation" or "for comprehension" that you can use for user-defined types, and there are no higher-kinded types so you can't implement generic helper functions like traverse (there's no way to write the type signature it should have).
- kllrnohj 8y agoYou can do something like this: class MyTransaction { fun begin() {} fun commit() {} } fun MyTransaction.query() {} fun MyTransaction.update() {} inline fun <R> transaction(tblock: MyTransaction.() -> R): R { val t = MyTransaction() t.begin() try { return t.tblock() } finally { t.commit() } } fun test() { // update() doesn't compile, no such method val hello = transaction { query() update() query() "Hello" } println("$hello") // prints "Hello" } Does that not work for your usage for some reason? Is there a problem with this approach I'm not seeing?
- lmm 8y agoI must admit I don't entirely understand how your approach works, but it seems like it requires all the functions you want to use in transactions to live on MyTransaction (even if only as extension methods)? I want to be able to write normal functions including quite high-level ones that take some business objects and return MustHappenInTransaction[SomeOtherBusinessObject], and pass these return values through generic methods that don't know anything about transactions specifically.
- kllrnohj 8y agotypealias MustBeInTransaction = MyTransaction.() -> Unit fun makeObject(i: Int): MustBeInTransaction { return { /* do something with i or whatever in a transaction */ } } fun test() { var genericArray = arrayOf(1, 2, 3).map { makeObject(it) } //genericArray.forEach { it() } // fails to compile transaction { genericArray.forEach { it() } } } Like that?
- eeperson 8y agoIt just dawned on me that Kotlin receivers[1] and extension methods[2] are basically the equivalent to Scala implicit params and conversions respectively. Although it appears that receivers can only really be used with anonymous functions. This appears to be similar to implicit function types proposed for Dotty[3]. So the Dotty equivalent to the above would be: type MustBeInTransaction = implicit MyTransaction => Unit def makeObject(i: Int): MustBeInTransaction = { return { /* do something with i or whatever in a transaction */ } } def test(): Unit = { var genericArray = Array(1, 2, 3).map(makeObject) //genericArray.forEach { _() } // fails to compile transaction { genericArray.forEach { _() } } } EDIT - I just noticed that this is almost exactly the same as the example used in the function types link below [1] https://stackoverflow.com/questions/45875491/what-is-a-receiver-in-kotlin https://stackoverflow.com/questions/45875491/what-is-a-recei... [2] https://kotlinlang.org/docs/reference/extensions.html https://kotlinlang.org/docs/reference/extensions.html [3] https://www.scala-lang.org/blog/2016/12/07/implicit-function-types.html https://www.scala-lang.org/blog/2016/12/07/implicit-function...