kartoffelsup
12/15/2019, 12:12 PM*>
and <*
? -> https://hackage.haskell.org/package/base-4.12.0.0/docs/Prelude.html#v:-42--62-
I'm trying to implement/follow in Arrow.Jannis
12/15/2019, 12:22 PM*>
in Monad for some reason. It's called followedBy
there. Imo we really need to move it to Apply
instead. There is no version of <*
in arrow afaik.
https://hackage.haskell.org/package/base-4.12.0.0/docs/src/GHC.Base.html#%2A%3E
has he implementation in terms of ap
,or <*>
as it's called in haskell land and it should be easy to port them to kotlin I think.Miguel Coleto
12/15/2019, 12:23 PMAlternative
because that is what is used in haskell to do applicative parsingMiguel Coleto
12/15/2019, 12:24 PMkartoffelsup
12/15/2019, 12:24 PMJannis
12/15/2019, 12:25 PMkartoffelsup
12/15/2019, 12:26 PMJannis
12/15/2019, 12:30 PMApply
typeclass...Jannis
12/15/2019, 12:31 PMkartoffelsup
12/15/2019, 12:40 PMJannis
12/15/2019, 12:44 PMliftA2
which also has a default implementation.
In short just follow the functions from *>
down to liftA2
they are all in a way implemented with <*>
, if you need help with it just ask, haskell code full of operators is not easy to read πkartoffelsup
12/15/2019, 12:45 PMJannis
12/15/2019, 1:49 PMkartoffelsup
12/15/2019, 1:53 PMkartoffelsup
12/15/2019, 1:55 PMfun <A, B, C> Kind<ForParser, A>.liftA2(a: Kind<ForParser, A>, b: Kind<ForParser, B>, f: (A, B) -> C)
: Parser<C> {
return b.ap(a.fix().map(f.curry())).fix()
}
fun <A, B> Kind<ForParser, A>.leftWins(right: Kind<ForParser, B>): Parser<A> = liftA2(this, right) { a, _ -> a }
fun <A, B> Kind<ForParser, A>.rightWins(right: Kind<ForParser, B>): Parser<B> = liftA2(this, right) { _, b -> b }
Jannis
12/15/2019, 1:57 PMApply.map
btw ^^Jannis
12/15/2019, 1:58 PMkartoffelsup
12/15/2019, 2:01 PMJannis
12/15/2019, 2:01 PMleftWins
is followedBy
and rightWins needs a good nameJannis
12/15/2019, 2:01 PMJannis
12/15/2019, 2:01 PMJannis
12/15/2019, 2:02 PMJannis
12/15/2019, 2:02 PMJannis
12/15/2019, 2:04 PM*>
and <*
so they are implemented a little different there. That is something kotlin and scala cannot have anywaykartoffelsup
12/15/2019, 3:06 PMJannis
12/15/2019, 3:07 PMJannis
12/15/2019, 3:13 PMJannis
12/15/2019, 3:15 PMkartoffelsup
12/15/2019, 3:16 PMkartoffelsup
12/15/2019, 3:33 PMval seq: SequenceK<Tuple2<String, A>>
into
val seq2: Tuple2<String, SequenceK<A>>
? I guess I need a Applicative for A which I can't provide π doesn't seem to work this wayJannis
12/15/2019, 3:33 PMrunParser.fold({
(input toT emptySequence()).some()
}, { (r, a) -> many().runParser(r).fold({ r toT sequenceOf(a) }, { (r2, xs) -> r2 toT (sequenceOf(a) + xs) }) })
This might work. Wrote this here in slack, so no idea about types etc, but the idea here is to skip the call to some and only recurse with many. This is still not great because it will always fully evaluate the whole thing till failure but it might work. ^^
What many
does is it basically re-runs a parser untill it fails and collects the results along the way. The first fold checks if the current iteration succeeds and the second one if the recursive rest succeeded or failedJannis
12/15/2019, 3:35 PMJannis
12/15/2019, 3:35 PMJannis
12/15/2019, 3:36 PMkartoffelsup
12/15/2019, 3:36 PMkartoffelsup
12/15/2019, 3:37 PMJannis
12/15/2019, 3:40 PMJannis
12/15/2019, 3:44 PMkartoffelsup
12/15/2019, 3:45 PMJannis
12/15/2019, 3:46 PMkartoffelsup
12/15/2019, 3:47 PMJannis
12/15/2019, 4:41 PMkartoffelsup
12/15/2019, 4:42 PMkartoffelsup
12/15/2019, 4:44 PMJannis
12/15/2019, 4:44 PMkartoffelsup
12/15/2019, 4:45 PMat io.github.kartoffelsup.json.JsonParserKt.stringParser(JsonParser.kt:56)
at io.github.kartoffelsup.json.JsonParserKt.jsonNull(JsonParser.kt:71)
at io.github.kartoffelsup.json.JsonParserKt.jsonValue(JsonParser.kt:115)
at io.github.kartoffelsup.json.JsonParserKt.jsonArray(JsonParser.kt:108)
Guess it's time to throw the towel π’kartoffelsup
12/15/2019, 4:45 PMkartoffelsup
12/15/2019, 5:01 PMjimn
12/15/2019, 5:09 PMkartoffelsup
12/15/2019, 5:10 PMjimn
12/15/2019, 5:11 PMjimn
12/15/2019, 5:12 PMjimn
12/15/2019, 5:19 PMkartoffelsup
12/15/2019, 5:27 PMjimn
12/15/2019, 5:38 PMjimn
12/15/2019, 5:38 PMjimn
12/15/2019, 5:41 PMJannis
12/15/2019, 6:44 PMkartoffelsup
12/15/2019, 8:52 PMJannis
12/15/2019, 9:18 PMlazyAp
as an operator. You can find the parser code in the KParsec file and a parser with it in the Parser file. The point of this was to parse kotlin data class show output into a generic format, diff it with other show output and pretty print it for awesome looking test failure reports. I'll redo this when I have a good solution for a stacksafe execution of that parser. https://github.com/1Jajen1/propCheck/pull/26/commits/2f45b8d6ee33c222f1e3547bd714a3c07e7b2a96Jannis
12/15/2019, 9:18 PMJannis
12/15/2019, 9:19 PMJannis
12/15/2019, 9:19 PMJannis
12/15/2019, 9:21 PMJannis
12/15/2019, 9:22 PMI
with String
and M<A>
with Option<A>
you have almost the same thingJannis
12/15/2019, 9:25 PMTrampoline
calls, explicit stack jumps by counting stack depth or if arrow-meta has some options by using those, but this parser code is definitly coming back π―Jannis
12/15/2019, 9:32 PMkartoffelsup
12/15/2019, 9:58 PMkartoffelsup
12/21/2019, 9:11 AMkartoffelsup
12/21/2019, 9:12 AMap
is really hard to grasp for meJannis
12/21/2019, 10:29 AMKind<F, A>
and a Kind<F, B>
an a function to combine them (A) -> (B) -> C
and then create a Kind<F, C>
. Looking back at ap
, it is basically the same thing, but the function is already partially applied inside F
. As for semantics, it is again best to look at Applicative.map
: Applicative allows to combine independent `F`'s, some thing Functor
can't do.kartoffelsup
12/22/2019, 12:58 AMJannis
12/22/2019, 1:02 AMJannis
12/22/2019, 1:02 AMJannis
12/22/2019, 1:03 AMkartoffelsup
12/22/2019, 1:03 AMkartoffelsup
12/22/2019, 1:03 AMkartoffelsup
12/22/2019, 1:03 AMJannis
12/22/2019, 1:03 AMkartoffelsup
12/22/2019, 1:04 AMJannis
12/22/2019, 1:04 AMkartoffelsup
12/22/2019, 1:04 AMJannis
12/22/2019, 1:08 AMkartoffelsup
12/22/2019, 1:10 AMJannis
12/22/2019, 1:15 AMJannis
12/22/2019, 1:16 AMkartoffelsup
12/22/2019, 1:16 AMJannis
12/22/2019, 1:17 AMkartoffelsup
12/22/2019, 1:17 AMjimn
12/22/2019, 1:18 AMJannis
12/22/2019, 1:18 AMkartoffelsup
12/22/2019, 1:19 AMJannis
12/22/2019, 1:19 AMjimn
12/22/2019, 1:20 AMjimn
12/22/2019, 1:20 AMJannis
12/22/2019, 1:21 AMjimn
12/22/2019, 1:21 AMJannis
12/22/2019, 1:21 AMjimn
12/22/2019, 1:21 AMJannis
12/22/2019, 1:23 AMkartoffelsup
12/22/2019, 1:23 AMkartoffelsup
12/22/2019, 1:23 AMjimn
12/22/2019, 1:27 AMJannis
12/22/2019, 1:28 AMjimn
12/22/2019, 1:31 AMJannis
12/22/2019, 1:32 AMJannis
12/22/2019, 1:33 AMjimn
12/22/2019, 1:35 AMjimn
12/22/2019, 1:35 AMjimn
12/22/2019, 1:37 AMJannis
12/22/2019, 1:40 AMJannis
12/22/2019, 1:40 AMjimn
12/22/2019, 1:41 AMjimn
12/22/2019, 1:44 AMkartoffelsup
12/22/2019, 1:33 PMJannis
12/23/2019, 1:21 AMkartoffelsup
12/23/2019, 1:54 AMkartoffelsup
12/23/2019, 10:33 AMval runParser: AndThen<StringView, Option<Tuple2<StringView, A>>>
fun <B> ap(ff: ParserOf<(A) -> B>): Parser<B> = Parser(
ff.fix().runParser.map { op ->
op.flatMap { (input2: StringView, f: (A) -> B) ->
// I need to ues input2 here or I'm stuck in an infinite loop
// but calling the AndThen here leads to the same stackoverflow
fix().runParser(input2).map { (input3: StringView, g: A) ->
Tuple2(input3, f(g))
}
}
}
)
vs
fun <B> ap(ff: ParserOf<(A) -> B>): Parser<B> = Parser(
ff.fix().runParser.flatMap { op ->
fix().runParser.map { op2 ->
op.flatMap { (input2: StringView, f: (A) -> B) ->
// infinite loop due to the result of ff (input2) being ignored
op2.map { (input3: StringView, g: A) ->
Tuple2(input3, f(g))
}
}
}
}
)
Jannis
12/23/2019, 11:15 AMParser(this.runParser.map { (Str, a) -> val (out, f) = ff.runParser.invoke(Str); out tot f(a) }) why does
slack also pull its bs code block stuff on mobile. Also this ignores the option, but I think that's easy enough to add :)kartoffelsup
12/23/2019, 11:31 AMinternal fun stringParser(string: String): Parser<String> {
val parser: Parser<ListK<Char>> = string
.map(::charParser).k()
.traverse(ParserApplicativeInstance) { it.fix() }.fix()
return parser.map { it.s() }
}
Jannis
12/23/2019, 11:52 AMJannis
12/23/2019, 1:16 PMmap2
. That has been fixed. Also there are now both tests which check the order of traverse against a left-right applicative and tests which check if the order ap is consistent with monad derived ones (although the pr to fix all of those instances is still open). If you want to proceed without using the snapshot build of arrow then just redefine traverse
like this (for lists, and AP is any applicative) foldRight(Eval.now(AP.just(emptyList()))) { v, acc -> Eval.later { f(v).ap(AP.run { acc.value().map { xs -> { x: A -> listOf(x) + xs } } }) } }
. That is almost the definition that is currently on master, the only difference is that it uses lazyAp
to avoid iterating the entire list if the left argument to ap fails (if it is Either.Left for example). Or alternatively you could change the dep to the snapshot build and use traverse
from there I thinkJannis
12/23/2019, 1:17 PMap
evaluates first.kartoffelsup
12/23/2019, 1:25 PMJannis
12/23/2019, 1:28 PMkartoffelsup
12/23/2019, 3:31 PMfun <B> lazyAp(ff: () -> ParserOf<(A) -> B>): Parser<B> = Parser(
fix().runParser.map { op ->
op.flatMap { (input, a) ->
ff().fix().runParser(input).map { (out, f) ->
out toT f(a)
}
}
})
I need to replace runParser(input) but I don't know with what :DJannis
12/28/2019, 12:27 AMjsonArray
for example you call jsonValue
outside of a lazy code block which invokes it, which then calls jsonArray
again etc. Invoking in lazyAp
could be fine for most things. I got my parser running now (had to switch the encoding + usage of AndThen
). It parses long-ish input and kept the exact same api. Not sure if it's stacksafe yet, probably need to do an extreme test (maybe with writing a json parser with it ^-^). I'll probably move it to a repo in a few days so I can work on it independent from propCheck, for now it is here: https://github.com/1Jajen1/propCheck/tree/rewrite-structure/src/main/kotlin/propCheck/pretty/parse The encoding is now a bit easier to read, but it is still a lot of code + nice error handling makes the code complex in a few places.kartoffelsup
12/28/2019, 7:55 AMJannis
12/28/2019, 8:20 AMkartoffelsup
12/28/2019, 8:21 AMJannis
12/28/2019, 9:08 AMJannis
12/28/2019, 10:35 AMJannis
12/28/2019, 10:36 AMJannis
12/28/2019, 11:28 AMStateT<ForOption, StringView, A>
which (according to arrows tests is stacksafe), that is why I think your parser should be able to do that as well. StateT<M, I, A> = (S) -> Kind<M, Tuple2<S, A>> => (StringView) -> Option<Tuple2<StringView, A>>
. So technically you could just have data class Parser<A>(val unParser: StateT<ForOption, StringView, A>)
and just use all methods from StateT
(I think it even has an Alternative
implementation). But still this won't help if your parser recurses before consuming input, because it will never short circuit in that case.Jannis
12/28/2019, 10:01 PMStateT
(even the uses of AndThen
) this could mean that long StateT
computations are also stackunsafe with the wrong monad. Because my parser is monadic I can use a stacksafe monad to make it stacksafe again. Eval
Trampoline
IO
all work. Your parser is a bit lost in that regard. All you can do is model it as (S) -> Kind<M, Option<Tuple2<S, A>>>
where M
is some stacksafe monad. This also means that State
in arrow is stack-unsafe because it is defined with Id
. Ticket time ^^kartoffelsup
12/28/2019, 11:31 PMJannis
12/28/2019, 11:51 PMkartoffelsup
01/03/2020, 2:57 PMJannis
01/03/2020, 7:20 PMkartoffelsup
01/03/2020, 9:54 PMJannis
01/03/2020, 10:45 PMJannis
01/03/2020, 10:45 PM