Beginner question: I am trying to copy the haskell...
# arrow
b
Beginner question: I am trying to copy the haskell interact function: interact :: (String -> String) -> IO () The 
interact
 function takes a function of type 
String->String
 as its argument. The entire input from the standard input device is passed to this function as its argument, and the resulting string is output on the standard output device. I got something like:
Copy code
fun interact(f: (String) -> String): IO<Unit> = IO.fx {
    f.apply { readLine() }
}
but now I am unsure how to proceed.
j
Interact isn't that easy, it reads all input till it reaches eof or the input handle is closed exceptionally etc. I am not sure how to do this in the jvm world tho. Probably treating the input as some sort of input-stream, consume it fully by accumulating and then apply the function to the accumulated input. Regarding `IO`: Since this is an impure operation it should be done within
IO
, however the task itself is not specific to
IO
.
e
Or, well, you could just write tailrec fun on IO 🤔 Not sure how to make it stack-safe tho
😕 1
b
Thanks, glad I am not dumb. My goal is to write a one liner like below that reads numbers and adds them together: Haskell: main = interact $ show . sum . map read . words How would you go about that? Mustn't necessarily be a one liner ...
e
Oh, my bad, it reads the entire input
b
Thanks Egor, but I am not sure I follow ... 😅
e
No, I just misunderstood what
interact
does
Although, in Haskell lists are lazy and String is a list of chars, so in Kotlin it would rather be
Sequence<Char>
. So I assume you may create an
IO
and somehow transform the operation of reading chars from java
InputStream
to Sequence of chars and go from there
j
Stacksafety is no concern when all you do is flatMap/map etc with IO recursively
even in haskell it reads the entire thing first, with or without laziness, so just keep it string. Not sure if the haskell version does lazyIO, but emulating that is a bad idea in a non-lazy language.
although @Egor Trutenko is not wrong in that if haskell does lazy IO it will work similar to reading chars from an input stream, but you still need to accumulate the entire thing because the result requires the entire input to be read anyway
For example:
interact (show . head)
will only output the first char of what ever has been read, but since the haskell version reads everything first with IO and the output is sequenced after that, the effect of reading has to be fully executed first before it can print.
if you want to work line by line the best thing to do is:
Copy code
fun interact(f: (String) -> String): IO<Unit> = IO.fx {
  val line = effect { readLine() }.bind()
  effect { println(f(line)) }.bind()
  interact(f).bind()
}
p
@Bruno apply here just sets
f
to the
this
scope, it doesn't
apply
the function in haskell terms. What you're looking for is f.
invoke
, or just
f(readLine())
👍 1
b
Thanks for all your replies. I still have trouble. If I solved the problem imperatively I would do do it like this:
Copy code
fun add(a: Int, b: Int): Int = a + b

fun main(args: Array<String>) {
    val sc = Scanner(System.`in`)
    val num1 = sc.nextInt()
    val num2 = sc.nextInt()
    val sum = add(num1, num2)
    println(sum)
}
p
Copy code
suspend fun main(args: Array<String>) = IO.fx {
    val sc = Scanner(System.`in`)
    val nextInt = effect { sc.nextInt() }
    val added = IO.mapN(nextInt, nextInt) { a, b -> add(a, b) }.bind()
    effect { println(added) }.bind()
}.run { suspended() }
👍 1
😮 1
that
run
comes from the
stdlib
, and it binds
this
to the caller inside the
{}
block. It's equivalent to
myIO.let { c: IO<Unit> -> c.suspended() }
j
This is much better than what I was about to write ^^ The difference here is that
IO
is lazy, defers side-effects and is an immutable value. You can move it around (as seen with
nextInt
) and compose it effortlessly. The same cannot be said for the non-io version of this program.
👍 1
b
Could you add the imports, because my Intellij does not like mapN and suspended(). For the latter it wants to import
Copy code
import sun.management.snmp.jvminstr.JvmThreadInstanceEntryImpl.ThreadStateMap.Byte0.suspended
I tried with
Copy code
"0.10.4-SNAPSHOT"
and
Copy code
"0.10.5-SNAPSHOT"
Nvm it works with "parMapN"
p
I'm at work, I cannot. Try
fix()
.suspended() because it's a member of IO afaik
👍 1
mapN may be in IO.applicative().mapN
j
parMapN
is a bad decision because it might execute out of order. @pakoito is refering to
Applicative.map
which you can get with
IO.applicative().map
. There is a ticket for renaming it to
mapN
but atm it is still called
map
👌🏼 1
👍🏼 1
b
Thnaks again for this ... somehow this still doesn't work for me. I appologise if it is a stupid mistake
message has been deleted
my imports:
Copy code
import <http://arrow.fx.IO|arrow.fx.IO>
import arrow.fx.extensions.fx
import <http://arrow.fx.extensions.io|arrow.fx.extensions.io>.applicative.applicative
import java.util.*
j
IO.applicative().map(io1, io2) { (a, b) -> add(a, b) }
. The parameter passed to
Applicative.map
is always a
TupleN
which you can destructure, not multiple parameters
👍 1
❤️ 1