jimn
10/30/2019, 6:21 AMjimn
10/30/2019, 6:41 AMjimn
10/30/2019, 7:36 AMhttps://www.youtube.com/watch?v=uyqqoooKpmI&lc=UgxYVFk4CGJ389AAx2J4AaABAg▾
Attila Domokos
11/03/2019, 3:42 AMEither.catch
looks like in this context?
I understand Try { }
is essentially the same, but it still reads better than try/catch
.Devesh Shetty
11/03/2019, 6:11 AMlose
was not added to Decidable? https://hackage.haskell.org/package/contravariant-1.4.1/docs/Data-Functor-Contravariant-Divisible.html#v:lose
https://github.com/arrow-kt/arrow/blob/master/modules/core/arrow-core-data/src/main/kotlin/arrow/typeclasses/Decidable.ktraulraja
11/03/2019, 10:59 AMTry
is deprecated in favor of IO and suspend. Changes like this are coming to other data types to eliminate nesting and improve ergonomics. Once Arrow Meta is available if you choose to use it you can entirely eliminate Either
and all result types in Kotlin of Arbitrary arity. We are working on a plugin for Arrow Meta for Union Types. These union types are Coproducts of arbitrary arity that can be used without nesting:
@optics
@recursion
typealias Result<A> = Union<Error, A> // Union<A, B, ...> synthetic and of arbitrary arity up to 22
val a: Result<Int> = error //ok, no need to error.left()
val b: Result<Int> = 1 // ok, no need to 1.right()
val c: Int? = a // ok because it's an error becomes `null`
val d: Int? = b // ok because it's an Int and stays Int
val e: Result<Int> = "other type" // not ok because not an error or an Int. String is not part of the union
val exhaustiveChecks =
when (a) {
is Error -> TODO()
is Int -> TODO()
}
a.fold({ e: Error -> TODO() }, { n: Int -> TODO() }) //@recursion folds cata and recursion schemes
Result.error.msg.modify(a, String::trim) // @optics DSLs for all union and products
As you can see in the example above there is no notion of left/right constructors and the datatype type args won’t be constrained to 2
.
This also removes the need to use ADTs or sealed classes when your intention is just representing a disjoint union with types you don’t own or can’t modify. Additionally it will include a leaner runtime than Arrow Either or Arrow Coproduct because we can use the type checker in meta at compile time to determine when values are of the union and avoid allocation with unnecessary wrappingtschuchort
11/03/2019, 12:00 PMJannis
11/03/2019, 1:04 PMjulian
11/06/2019, 2:53 PMkluck
11/06/2019, 2:55 PMarrow.fx.internal.Platform$_trampoline$1
really seems Platform
-related after all... Any thoughts?tmg
11/07/2019, 3:02 PMsimon.vergauwen
11/08/2019, 2:50 PMclass ForResult private constructor() { companion object }
typealias ResultOf<A> = arrow.Kind<ForResult, A>
@Suppress("UNCHECKED_CAST", "NOTHING_TO_INLINE")
inline fun <A> ResultOf<A>.fix(): Result<A> = this as Result<A>
Ryan Benasutti
11/08/2019, 4:40 PMpakoito
11/08/2019, 5:17 PMrace
with an IO.sleep
julian
11/08/2019, 6:42 PMMiguel Coleto
11/08/2019, 8:15 PMjulian
11/09/2019, 7:13 PMpakoito
11/10/2019, 7:09 PMpakoito
11/12/2019, 10:55 AMGreg Hibberd
11/13/2019, 9:14 PMfun ListK<Tuple2<String, Int>>.modifyListItem(key: String, amount: Int) = Option.fx {
//Find the item
val index = indexOfFirst { it.a == key }.let { if(it == -1) None else Some(it) }.bind()
//Get the item
val item = get(index)
//Modify
val modified = itemLens.set(item, item.b + amount)
//Create an index
val listIndex = ListK.index<Item>().index(index)
//Replace the item
listIndex.set(this@modifyListItem, modified)
}
pakoito
11/13/2019, 9:36 PMlist.map {
if (it.a == key) {
...
} else {
it
}
}
Stian N
11/14/2019, 10:30 AMpakoito
11/14/2019, 3:12 PMpakoito
11/14/2019, 4:48 PMNikita Smolenskii
11/15/2019, 9:28 AMEither.catch
that forces suspend.
Article provides following code snippet, that somehow correate with current implementation https://github.com/arrow-kt/arrow/blob/b332574a969497dc9f072d52bbb5eea60081a9fe/modules/core/arrow-core-data/src/main/kotlin/arrow/core/Either.kt#L859
suspend fun <A> Either.Companion.catch(f: suspend () -> A): Either<Throwable, A> =
try { f().right() } catch (t: Throwable) { t.left() }
Is there any particular reason to not do instead, that allow catch
function to be suspend
agnostic
inline fun <A> Either.Companion.catch(f: () -> A): Either<Throwable, A> =
try { f().right() } catch (t: Throwable) { t.left() }
with that decalration, both will be possible, with initial declaration - second one only
fun blocking(): Int = 42
suspend fun suspended(): Int = 42
fun main() {
Either.catch { "first: ${blocking()}" }
runBlocking {
Either.catch { "second: ${suspended()}" }
}
}
Why arrow team forces to use coroutines everywhere? Even if it’s not needed.Jannis
11/15/2019, 9:32 AMabendt
11/15/2019, 7:29 PMJannis
11/15/2019, 7:32 PMJohan Basson
11/16/2019, 6:40 AMfun authenticate(
request: AuthenticateRequest,
secretKey: SecretKey,
con: Connection
): Either<ErrorMessage, Token>> = Either.fx {
val (optUser) = getUserByUsername(con, request.username)
val (user) = checkUserExists(optUser)
val (_) = checkPassword(request.password, user)
val (token) = generateToken(user, secretKey)
token
}
In this case secretKey and Connection is dependencies
From what I understand you can use the Reader monad to inject it from the top of the application:
fun authenticate(request: AuthenticateRequest): Reader<Context, Either<ErrorMessage, Token>> {
return ReaderApi.ask<Context>().map { ctx ->
Either.fx {
val (optUser) = getUserByUsername(ctx.sql2o, request.username)
val (user) = checkUserExists(optUser)
val (_) = checkPassword(request.password, user)
val (token) = generateToken(user, ctx.secretKey)
token
}
}
}
But this does not seem to compileJannis
11/17/2019, 7:44 AMJannis
11/17/2019, 7:44 AMsam
11/20/2019, 2:34 AMTry { "a".toInt() }
Jannis
11/20/2019, 11:21 AM"a".toInt()
has the type fun String.toInt(): Int
which would mean it would have to be defined for all strings possible. But it isn't as not all strings can be converted to ints. Thats a partial function and partial functions throw exceptions on unhandled input and are thus not pure. Throwing an exception is an action not indicated by the return type => definition of a side effect. fun Sring.toInt(): Int
is much better if it has either Option<Int>
or Either<SomeDescriptiveError, Int>
(or similar) as a return type. Coming back to Try
since String.toString(): Int
is impure and Try
does not suspend that side effect, the invocation of Try
is by definition also impure.Try
is the main purpose it was added for (as a simple wrapper around try catch), so using it that way is also not too bad when no other side-effects are involved. It's just that kotlin cannot prove that with it's type system, making this a bad abstraction because it's too easy to use it wrong. I usually follow two paths when working with exception throwing functions that I have no control over:
Wrap it in suspend because it might be doing side-effects or use try catch directly and convert to option/either for code that I know is otherwise pure. It's quite useful to define an extension function for that. The only reason arrow does not provide this is because it's again, like Try
, an abstraction that promotes misuse and we try to avoid that in arrow.sam
11/20/2019, 6:00 PMTry
value is such a function. I don’t believe there’s any requirement for the function to be total.
The reason I asked about this is because when people question why Try
was deprecated, I think the better answer is not “because it’s not pure” but because “we don’t want to encourage people to catch IO exceptions” or “we don’t want to encourage strict evaluation of effects”.Jannis
11/20/2019, 8:19 PMfun a(str: String) = Try { str.toInt() }
is total and deterministic and thus anything returning Try
is pure (if and only if there are no other side-effects apart from exceptions). That is not the problem with Try
though. The problem is anything within it has to be impure to be useful, and that is not what arrow wants to promote. As I said in my second message, wrapping impure code that has no other side effects and that you have no control over, is exactly what Try
is/was useful for and that's still how I use it (or an equivalent ext func to option or either). But as the deprecation message states: Try promotes use of impure functions (or something like that) and that is just bad in all cases.
My original message is indeed wrong. Try
is pure in all cases that only involve non-fatal exceptions (fatal exceptions are rethrown afaik).
So a good answer to "why is Try
deprecated?" is: "Because it promotes using and writing impure functions". The other part about IO or effects is also true, but not the only reason.IO
or Try
(or equivalent). If you know it has no side-effects other than exceptions (which is quite hard to figure out about code you do not own) then Try
is perfectly fine, for all other cases use IO
or equivalently suspendsam
11/20/2019, 8:23 PMJannis
11/20/2019, 8:23 PMsam
11/20/2019, 8:24 PMJannis
11/20/2019, 8:24 PMsam
11/20/2019, 8:24 PMJannis
11/20/2019, 8:24 PMsam
11/20/2019, 8:25 PMa.toInt()
fulfils the rules of purity - output is always the same for the input and it has no side effects.Jannis
11/20/2019, 8:26 PMfun String.toInt(): Int
has no indication that it might throw, similar to fun Int.div(i: Int): Int
sam
11/20/2019, 8:26 PMpublic Int toInt(String input) throws NumberFormatException
is that better ?Jannis
11/20/2019, 8:27 PMInt | NumberFormatException
. It has lots of other problems but it is something I'd consider puresam
11/20/2019, 8:28 PMJannis
11/20/2019, 8:29 PMsam
11/20/2019, 8:29 PMfun String.toInt(): Int
<-- it’s the same just the exception isn’t included in the return type, but it’s the same as the Java one.Jannis
11/20/2019, 8:30 PMsam
11/20/2019, 8:30 PMJannis
11/20/2019, 8:31 PMsam
11/20/2019, 8:31 PMJannis
11/20/2019, 8:31 PMsam
11/20/2019, 8:32 PMJannis
11/20/2019, 8:33 PMthrows NumberFormatException
. It needs to be in the signature/return type to be considered puresam
11/20/2019, 8:33 PMconst foo = (a) => a + 1
Jannis
11/20/2019, 8:34 PMsam
11/20/2019, 8:35 PMJannis
11/20/2019, 8:36 PMfun String.toInt() = toInt()
it's just as impuresam
11/20/2019, 8:36 PMJannis
11/20/2019, 8:36 PMsam
11/20/2019, 8:37 PMJannis
11/20/2019, 8:38 PMsam
11/20/2019, 8:39 PMJannis
11/20/2019, 8:39 PMsam
11/20/2019, 8:40 PMJannis
11/20/2019, 8:40 PMsam
11/20/2019, 8:40 PMJannis
11/20/2019, 8:40 PMfun a(arg: A): B
I do not want this to throw eversam
11/20/2019, 8:40 PMJannis
11/20/2019, 8:41 PMsam
11/20/2019, 8:41 PMJannis
11/20/2019, 8:41 PMsam
11/20/2019, 8:41 PMTry
and things like that is because they tend to hide effectsJannis
11/20/2019, 8:42 PMsam
11/20/2019, 8:42 PMJannis
11/20/2019, 8:44 PMTry
is pure, code that is useful to use inside Try
and has no other side effects is not. Btw thanks for pointing out that Try
is pure, I had that wrong first. 🙂sam
11/20/2019, 8:46 PMJannis
11/20/2019, 8:46 PMsam
11/20/2019, 8:46 PMJannis
11/20/2019, 8:46 PMsam
11/20/2019, 8:47 PMJannis
11/20/2019, 8:47 PMsam
11/20/2019, 8:48 PMJannis
11/20/2019, 8:48 PMsam
11/20/2019, 8:51 PMJannis
11/20/2019, 8:51 PMval genA = Gen.fx {
val length = int(0..100).bind()
string().list(length)
}
this is a perfectly fine generator for creating lists strings, however when shrinking it will first shrink the length and then the elements inside the list. That means once it shrunk the content list it cannot go back to shrinking the lengthsam
11/20/2019, 8:53 PMGen.ints().map { … }
Jannis
11/20/2019, 8:53 PMsam
11/20/2019, 8:55 PMJannis
11/20/2019, 8:55 PMflatMap
the result is a function, so to shrink it it has to first evaluate it, but you cannot go back after doing soclass Rose<A>(val el: A, val branches: Sequence<Rose<A>>)
Getting them to work nicely with a decent api is quite a bit of work tho.sam
11/20/2019, 8:59 PMJannis
11/20/2019, 8:59 PMsam
11/20/2019, 9:00 PMJannis
11/20/2019, 9:01 PMsam
11/20/2019, 9:02 PMJannis
11/20/2019, 9:03 PMflatMap
require quite a bit of work to overcome. The solution is two fold: Don't ever use it unless you need it, and if you need it either ignore that you have worse shrinking or add you own shrinking back 😕sam
11/20/2019, 9:06 PMJannis
11/20/2019, 9:08 PMsam
11/20/2019, 9:11 PM