How to create an alias to bind method in Arrow-kt? - kotlin

In Arrow-kt I'd like to create an alias to the bind() - for a kind of custom lib to use Arrow.
I'd expect the following to work but it doesn't:
suspend fun <F, S> Either<F, S>.bindMy(): S = this.bind()
The method I wanna target is
public interface EffectScope<in R> {
...
public suspend fun <B> Either<R, B>.bind(): B
I guess it doesn't work as I expect because of the EffectScope.
Any idea how I could make it work?
Thx

bind is defined in EffectScope or Raise (Arrow 2.0 snapshot) as an extension method over Either<A, B>.
You can go about this in different ways.
Use the upcoming context receivers feature if you are in JVM.
context(EffectScope<E>)
fun <E, A> Either<E, A>.myBind(): A = fold({ shift(it) }, ::identity)
Extend the EffectScope interface and define your fold machinery as Arrow does for EffectScope and Effect. Unfortunately, until context receivers are available, this is a more heavyweight solution. If you use the 2.0 snapshot, where all this is simpler, you will need to provide similar machinery like Raise and Effect.
If you'd like some help with any of this, we hang out in the Kotlin slack #Arrow channel

Related

Relation between Arrow suspend functions and monad comprehension

I am new to Arrow and try to establish my mental model of how its effects system works; in particular, how it leverages Kotlin's suspend system. My very vague understanding is as follows; if would be great if someone could confirm, clarify, or correct it:
Because Kotlin does not support higher-kinded types, implementing applicatives and monads as type classes is cumbersome. Instead, arrow derives its monad functionality (bind and return) for all of Arrow's monadic types from the continuation primitive offered by Kotlin's suspend mechanism. Ist this correct? In particular, short-circuiting behavior (e.g., for nullable or either) is somehow implemented as a delimited continuation. I did not quite get which particular feature of Kotlin's suspend machinery comes into play here.
If the above is broadly correct, I have two follow-up questions: How should I contain the scope of non-IO monadic operations? Take a simple object construction and validation example:
suspend fun mkMessage(msgType: String, appRef: String, pId: String): Message? = nullable {
val type = MessageType.mkMessageType(msgType).bind()
val ref = ApplRefe.mkAppRef((appRef)).bind()
val id = Id.mkId(pId).bind()
Message(type, ref, id)
}
In Haskell's do-notation, this would be
mkMessage :: String -> String -> String -> Maybe Message
mkMessage msgType appRef pId = do
type <- mkMessageType msgType
ref <- mkAppRef appRef
id <- mkId pId
return (Message type ref id)
In both cases, the function returns the monad type (a nullable value, resp. Maybe). However, while I can use the pure function in Haskell anywhere I see fit, the suspend function in Kotlin can only be called from within a suspend function. In this way, a simple, non-IO monad comprehension in Arrow behaves like an IO monad that must be threaded throughout my code base; I suppose this results because the suspend mechanism was designed for actual IO operations. What is the recommended way to implement non-IO monad comprehensions in Arrow without making all functions into suspend functions? Or is this actually the way to go?
Second: If in addition to non-IO monads (nullable, reader, etc.), I want to have IO - say, reading in a file and parsing it - how would i combine these two effects? Is it correct to say that there would be multiple suspend scopes corresponding to the different monads involved, and I would need to somehow nest these scopes, like I would stack monad transformers in Haskell?
The two questions above probably mean that I am still lacking a mental model that bridges between the continuation-based implementation atop the Kotlin's suspend mechanism with the generic monad-as-typeclass implementation in Haskell.
schuster,
You're correct that Arrow uses the suspension feature from Kotlin to encode something like monad comphrensions.
To answer your first question:
Kotlin has suspend in the language (and Kotlin Std), by default suspend can only be called from other suspend code. However, the compiler also has a feature called RestrictsSuspension, this disallows for mixing suspend scopes and thus disallows the ablity to combine IO and Either for example. We expose a secondary DSL, either.eager which is encoded using RestrictsSuspension and it disallows calling foreign suspend functions.
This allows you to encode mkMessage :: String -> String -> String -> Maybe Message.
fun mkMessage(msgType: String, appRef: String, pId: String): Message? = nullable.eager {
val type = MessageType.mkMessageType(msgType).bind()
val ref = ApplRefe.mkAppRef((appRef)).bind()
val id = Id.mkId(pId).bind()
Message(type, ref, id)
}
To answer your second question:
IO as a data type is not needed in Kotlin, since suspend can implement all IO operations in a referential transparent way like it works in Haskell.
The compiler also makes a lot optimisations in the runtime, just like Haskell does for IO.
So the signature suspend fun example(): Either<Error, Value> is the equivalent of EitherT IO Error Value in Haskell.
The IO operations are however not implemented in the Kotlin Std, but in a library KotlinX Coroutines, and Arrow Fx Coroutines also offers some data types and higher-level operations such as parTraverse defined on top of KotlinX Coroutines.
It's slightly different than in Haskell, since we can mix effects instead of stacking them with monad transformers. This means that we can call IO operations from within Either operations. This is due to special functionality, and optimisations the compiler can make in the suspension system. This blog explains how that optimisation works, and why it's so powerful. https://nomisrev.github.io/inline-and-suspend/
Here is also some more background on Continuations, and tagless encodings in Kotlin. https://nomisrev.github.io/continuation-monad-in-kotlin/
I hope that fully answers your question.
I don't think I can answer everything you asked, but I'll do my best for the parts that I do know how to answer.
What is the recommended way to implement non-IO monad comprehensions in Arrow without making all functions into suspend functions? Or is this actually the way to go?
you can use nullable.eager and either.eager respectively for pure code. Using nullable/either (without .eager) allows you to call suspend functions inside. Using eager means you can only call non-suspend functions. (not all effectual functions in kotlin are marked suspend)
Second: If in addition to non-IO monads (nullable, reader, etc.), I want to have IO - say, reading in a file and parsing it - how would i combine these two effects? Is it correct to say that there would be multiple suspend scopes corresponding to the different monads involved, and I would need to somehow nest these scopes, like I would stack monad transformers in Haskell?
You can use extension functions to emulate Reader. For example:
suspend fun <R> R.doSomething(i: Int): Either<Error, String> = TODO()
combines Reader + IO + Either. You can find a bigger example here from Simon, an Arrow maintainer.

Reflection and Generics in Kotlin

I've written myself into a corner where I want an instance of Class<Foo<Bar>>. While there's no apparent reason that this shouldn't be valid, there seems to be no way to create one. Foo<Bar>::class.java is a syntax error, and Kotlin does not provide a public constructor for Class.
The code I'm writing is an abstraction layer over gson. Below is an overly-simplified example:
class Boxed<T : Any> (val value: T)
class BaseParser<U : Any> (
private val clazz: Class<U>
) {
//This works for 98% of cases
open fun parse(s: String): U {
return gson.fromJson(s, clazz)
}
//Presume that clazz is required for other omitted functions
}
//Typical subclass:
class FooParser : BaseParser<Foo>(Foo::class.java)
// Edge Case
class BarParser : BaseParser<Boxed<Bar>>(Boxed<Bar>::class.java) {
override fun parse(s: String): Boxed<Bar> {
return Boxed(gson.fromJson(s, Bar::class.java))
}
}
// not valid: "Only classes are allowed on the left hand side of a class literal"
In my production code, there are already dozens of subclasses that inherit from the base class, and many that override the "parse" function Ideally I'd like a solution that doesn't require refactoring the existing subclasses.
Actually, there is a reason this is impossible. Class (or Kotlin's KClass) can't hold parameterized types. They can hold e.g. List, but they can't List<String>. To store Foo<Bar> you need Type (or Kotlin's KType) and specifically ParameterizedType. These classes are somewhat more complicated to use and harder to acquire than simple Class.
The easiest way to acquire Type in Kotlin is by using its typeOf() utility:
typeOf<Foo<Bar>>().javaType
Gson supports both Class and Type, so you should be able to use it instead.
The closest you'll get is Boxed::class.java. This is not a language restriction but a JVM restriction. JVM has type erasure, so no generic types exist after compilation (thats also one of the reasons generics cant be primitives, as they need to be reference types to behave).
Does it work with the raw Boxed type class?
For this case, it looks like
BaseParser<Boxed<Bar>>(Boxed::class.java as Class<Boxed<Bar>>)
could work (that is, it will both type-check and succeed at runtime). But it depends on what exactly happens in the "Presume that clazz is required for other omitted functions" part. And obviously it doesn't allow actually distinguishing Boxed<Foo> and Boxed<Bar> classes.
I'd also consider broot's approach if possible, maybe by making BaseParser and new
class TypeBaseParser<U : Any>(private val tpe: Type)
extend a common abstract class/interface.

Call reified function from non-reified one

I've got a third-party service that exposes a reified extension function. I would like to wrap this service into an interface-implementation pair for easier testing and encapsulation reason.
The problem is that I can't use this function in my app, because compiler tells me that:
Cannot use 'T' as reified type parameter. Use a class instead.
The structure is like following:
interface ThirdPartyService
inline fun <reified T> ThirdPartyService.execute(): T
interface Wrapper {
fun <T> execute(): T
}
class WrapperImpl(private val thirdPartyService: ThirdPartyService) : Wrapper {
override fun <T> execute(): T =
thirdPartyService.execute()
}
In this case calling thirdPartyService.execute() causes a compiler issue I mentioned above.
Is there any way to overcome this issue? What does this Use a class instead. actually means?
When you mark a type as reified, you're allowing it to be used explicitly in the function in places that it would normally be erased.
However the type T in your Wrapper.execute function here is not reified which means that it will be erased at runtime and therefore unable to be 'passed' to the ThirdPartyService.execute function.
You can read more about reified types here.
In essence, the whole point of a function with a reified type is that it can use the type at runtime. So calling it without a concrete type doesn't make any sense and due to type erasure, a non-reified T is not a concrete type.

Kotlin - delegate as function argument (anonymous instantiation)

Is it possible to create an anonymous delegate in Kotlin for the purpose of passing to a function argument? I'm particularly interested in by lazy, but this question probably applies to all delegates. For example, say I have this function:
fun sayHello(name: String){
println("Hello $name")
}
this works just fine:
val name by lazy{ "Ralph" }
sayHello(name)
But none of the following are correct:
sayHello(lazy{"Ralph"})
sayHello(by lazy{"Ralph"})
sayHello({"Ralph") as lazy})
Is this possible somehow?
There's not a practical way to do this for any general delegate. Delegates are designed for use specifically with properties, so their getter implementation takes an object instance (the property owner) and a KProperty argument (see ReadOnlyProperty). They might specifically need these references for their functionality.
The Lazy interface happens to have a value property so you can use it like this, but this does not apply to all delegates:
sayHello( lazy{"Ralph"}.value )

When should one prefer Kotlin extension functions?

In Kotlin, a function with at least one argument can be defined either as a regular non-member function or as an extension function with one argument being a receiver.
As to the scoping, there seems to be no difference: both can be declared inside or outside classes and other functions, and both can or cannot have visibility modifiers equally.
Language reference seems not to recommend using regular functions or extension functions for different situations.
So, my question is: when do extension functions give advantage over regular non-member ones? And when regular ones over extensions?
foo.bar(baz, baq) vs bar(foo, baz, baq).
Is it just a hint of a function semantics (receiver is definitely in focus) or are there cases when using extensions functions makes code much cleaner or opens up opportunities?
Extension functions are useful in a few cases, and mandatory in others:
Idiomatic Cases:
When you want to enhance, extend or change an existing API. An extension function is the idiomatic way to change a class by adding new functionality. You can add extension functions and extension properties. See an example in the Jackson-Kotlin Module for adding methods to the ObjectMapper class simplifying the handling of TypeReference and generics.
Adding null safety to new or existing methods that cannot be called on a null. For example the extension function for String of String?.isNullOrBlank() allows you to use that function even on a null String without having to do your own null check first. The function itself does the check before calling internal functions. See documentation for extensions with Nullable Receiver
Mandatory Cases:
When you want an inline default function for an interface, you must use an extension function to add it to the interface because you cannot do so within the interface declaration (inlined functions must be final which is not currently allowed within an interface). This is useful when you need inline reified functions, for example this code from Injekt
When you want to add for (item in collection) { ... } support to a class that does not currently support that usage. You can add an iterator() extension method that follows the rules described in the for loops documentation -- even the returned iterator-like object can use extensions to satisfy the rules of providing next() and hasNext().
Adding operators to existing classes such as + and * (specialization of #1 but you can't do this in any other way, so is mandatory). See documentation for operator overloading
Optional Cases:
You want to control the scoping of when something is visible to a caller, so you extend the class only in the context in which you will allow the call to be visible. This is optional because you could just allow the extensions to be seen always. see answer in other SO question for scoping extension functions
You have an interface that you want to simplify the required implementation, while still allowing more easy helper functions for the user. You can optionally add default methods for the interface to help, or use extension functions to add the non-expected-to-be-implemented parts of the interface. One allows overriding of the defaults, the other does not (except for precedence of extensions vs. members).
When you want to relate functions to a category of functionality; extension functions use their receiver class as a place from which to find them. Their name space becomes the class (or classes) from which they can be triggered. Whereas top-level functions will be harder to find, and will fill up the global name space in IDE code completion dialogs. You can also fix existing library name space issues. For example, in Java 7 you have the Path class and it is difficult to find the Files.exist(path) method because it is name spaced oddly. The function could be placed directly on Path.exists() instead. (#kirill)
Precedence Rules:
When extending existing classes, keep the precedence rules in mind. They are described in KT-10806 as:
For each implicit receiver on current context we try members, then local extension functions(also parameters which have extension function type), then non-local extensions.
Extension functions play really well with the safe call operator ?.. If you expect that the argument of the function will sometimes be null, instead of early returning, make it the receiver of an extension function.
Ordinary function:
fun nullableSubstring(s: String?, from: Int, to: Int): String? {
if (s == null) {
return null
}
return s.substring(from, to)
}
Extension function:
fun String.extensionSubstring(from: Int, to: Int) = substring(from, to)
Call site:
fun main(args: Array<String>) {
val s: String? = null
val maybeSubstring = nullableSubstring(s, 0, 1)
val alsoMaybeSubstring = s?.extensionSubstring(0, 1)
As you can see, both do the same thing, however the extension function is shorter and on the call site, it's immediately clear that the result will be nullable.
There is at least one case where extension functions are a must - call chaining, also known as "fluent style":
foo.doX().doY().doZ()
Suppose you want to extend the Stream interface from Java 8 with you own operations. Of course, you can use ordinary functions for that, but it will look ugly as hell:
doZ(doY(doX(someStream())))
Clearly, you want to use extension functions for that.
Also, you cannot make ordinary functions infix, but you can do it with extension functions:
infix fun <A, B, C> ((A) -> B).`|`(f: (B) -> C): (A) -> C = { a -> f(this(a)) }
#Test
fun pipe() {
val mul2 = { x: Int -> x * 2 }
val add1 = { x: Int -> x + 1 }
assertEquals("7", (mul2 `|` add1 `|` Any::toString)(3))
}
There are cases where you have to use extension methods. E.g. if you have some list implementation MyList<T>, you can write an extension method like
fun Int MyList<Int>.sum() { ... }
It is impossible to write this as a "normal" method.