nwh
06/18/2018, 1:52 AMinterface Flippable<T> {
fun flip(): T
}
extend String as Flippable<String> {
// Implements all of the Flippable properties/methods missing from the base class
fun flip(): String {
...
}
}
Then you could treat a String
as an instance of Flippable
wcaokaze
06/18/2018, 2:07 AMfun String.asFlippable(): Flippable<String>
?benleggiero
06/18/2018, 2:15 AMFlippable
, with the proposed way you continue using the string. With your way you're stuck with an opaque typewcaokaze
06/18/2018, 2:22 AMgildor
06/18/2018, 2:23 AMgildor
06/18/2018, 2:24 AMtschuchort
06/18/2018, 11:42 AMbenleggiero
06/18/2018, 12:13 PMbenleggiero
06/18/2018, 12:28 PMprotocol
(their word for interface
) conformance after a type has been declared. It's a very underrated and powerful language feature that basically changed the way I think about programming in general.
This lets the compiler more easily do very complex reasoning on types. For instance I've been wanting to create an interface of my own that I can later say all the number types (Int
, Number
, BigInteger
, etc) conform to so I can strongly differentiate between integer types and real number types without typechecking against all known classes (both tedious and fragile), which would let me do things that are absolutely impossible right now (like promise integer output given integer input without restricting myself to the caller's chosen bitwidth) because there's no way to express to the compiler that what I'm doing is guaranteed to succeed, even if it is.
I would love to see this added, and would love to help work on it too. I just can't promise time because I've been so busy with moving apartments that I can't even give my collection literals proposal enough time :(dalexander
06/18/2018, 12:33 PMcedric
06/19/2018, 4:46 AMtschuchort
06/19/2018, 10:51 AMkevinmost
06/19/2018, 1:05 PMval str = "foo"
if (str is Flippable<*>) { ... }
Is that condition true or false?tschuchort
06/19/2018, 3:37 PMstr
isn't a Flippable<*>
, it has a Flippable<String>
(implementation). An important distinction, since there may be different sensible implementations depending on the type class and you avoid the issue of redeclaration.benleggiero
06/19/2018, 3:52 PMis
should be changed to detect any implemented conformance, whether at the original declaration or via an extension later on. The exact location of the implementation should not matter to the API user.
I wouldn't have to say if (str is Flippable<*> || str has Flippable<*>)
everywhere I check for interface conformance. That's just dumbgildor
06/19/2018, 3:54 PMkevinmost
06/19/2018, 4:38 PMis
to also take magical implicit implementations into account, personallynwh
06/19/2018, 4:41 PMis
would have to return true there, it's the only logical behaviornwh
06/19/2018, 4:41 PMbenleggiero
06/19/2018, 4:42 PMis
should become smart enough to also check for that. No backwards breaking, no forwards breaking.tschuchort
06/19/2018, 9:16 PMis
I presume you would also cast to that type. How is the compiler supposed to decide which methods to call, since there may be multiple declarations of extension methods that implement the interface. It has to be decided at compile time what methods to callnwh
06/19/2018, 10:47 PMbenleggiero
06/19/2018, 10:54 PMgildor
06/19/2018, 11:37 PMgildor
06/19/2018, 11:46 PMbenleggiero
06/20/2018, 3:32 AMgildor
06/20/2018, 3:34 AMbenleggiero
06/20/2018, 3:34 AMassertApproximatelyEqual
function. Bunches less boilerplate. O(1)
instead of O(n)
gildor
06/20/2018, 3:36 AMbenleggiero
06/20/2018, 3:41 AM// In library A:
class Foo {
fun clone(): Foo { ... } // very okay
}
extension Foo: Comparable<Foo> { // very okay
override fun compareTo(other: Foo): Int { ... }
}
// In library B:
extension Foo: Comparable<Foo> // error: Foo already conforms to Comparable
extension Foo: Cloneable // okay; all requirements already met
extension Foo: CharSequence { // okay
override val length: Int { ... }
override fun get(index: Int): Char { ... }
override fun subSequence(startIndex: Int, endIndex: Int): Foo { ... }
}
benleggiero
06/20/2018, 3:43 AMgildor
06/20/2018, 3:43 AMgildor
06/20/2018, 3:44 AMgildor
06/20/2018, 3:44 AMgildor
06/20/2018, 3:45 AMbenleggiero
06/20/2018, 3:46 AMgildor
06/20/2018, 3:46 AMbenleggiero
06/20/2018, 3:47 AMgildor
06/20/2018, 3:47 AMgildor
06/20/2018, 3:48 AMbenleggiero
06/20/2018, 3:52 AM// In library C, which includes A as a dependency but does not know about B:
extension Foo: CharSequence { // okay
override val length: Int { ... }
override fun get(index: Int): Char { ... }
override fun subSequence(startIndex: Int, endIndex: Int): Foo { ... }
}
// In App D, which includes both A, B, and C:
import library.a.Foo // okay: basic functionality
import library.b.Foo // okay: extended functionality by B
import library.c.Foo // error: both B and C declare Foo's implementation of CharSequence
benleggiero
06/20/2018, 3:52 AMbenleggiero
06/20/2018, 3:53 AMgildor
06/20/2018, 3:53 AMgildor
06/20/2018, 3:54 AMbenleggiero
06/20/2018, 3:55 AMgildor
06/20/2018, 3:56 AMgildor
06/20/2018, 3:56 AMgildor
06/20/2018, 3:57 AMbenleggiero
06/20/2018, 4:02 AMgildor
06/20/2018, 5:03 AMgildor
06/20/2018, 5:03 AMgildor
06/20/2018, 5:04 AMgildor
06/20/2018, 5:11 AMraulraja
06/24/2018, 10:13 PMString
with the contract of being contrained by Flippable
. What is missing for type classes is to define how one can depend in such syntax since String is not a subtype of Flippable and String: Flippable
may not be a good bounds expression. How does one write a function or class that depends on there being a Flippable<T>
in the scope such as that the syntax is activated? Is it via import? or are they coherent in the sense that the compiler can guarantee a single instance? Are there any override capabilities for users to modify the behavior libraries provide as instances?raulraja
06/25/2018, 11:23 AMgildor
06/25/2018, 12:52 PMraulraja
06/25/2018, 1:17 PMinternal
override in call site definition package or companionraulraja
06/25/2018, 1:18 PMraulraja
06/25/2018, 1:19 PMraulraja
06/28/2018, 11:05 AM