https://kotlinlang.org logo
Join SlackCommunities
Powered by
# forkhandles
  • r

    Razvan

    04/22/2021, 8:01 PM
    Great new addons (need to play with partial as with just this definition not sure what it does). And thanks for the alias. Hope the imports will get it better the right one.
  • r

    Razvan

    04/22/2021, 8:07 PM
    The readme example makes it clear. Nice trick.
  • d

    dave

    05/01/2021, 1:20 PM
    We've just released ForkHandles v1.10.0.0 which upgrades to Kotlin 1.5.0 and adds support for the Kotlin Result type in values4k factories: https://github.com/fork-handles/forkhandles/blob/trunk/CHANGELOG.md
  • d

    dave08

    05/09/2021, 2:07 PM
    Hi! It might be nice to have a
    filter(predicate: () -> Boolean, error: () -> E)
    just like
    filterNotNull
    in Result4k... my use case is that in the case my result is an empty list, it should return a domain error. What do you guys think?
  • d

    dave08

    05/09/2021, 2:10 PM
    Also a
    inline fun <T, R> resultFrom(block: T.() -> R): Result<R, Exception>
    might also be nice (makes chaining a bit cleaner), like in kotlin.Result.
    n
    • 2
    • 6
  • r

    Razvan

    05/13/2021, 7:25 PM
    can values4k in the future take profit form 1.5's
    value class
    ?
  • d

    dave

    05/13/2021, 7:30 PM
    values4k in the present can profit from it! latest version is already on 1.5.0
  • r

    Razvan

    05/14/2021, 11:53 AM
    I saw the remark about inner classes in 1.5 thought it was about the RC as they changed the name since. So a Value<T> will be a mapped as in
    value class
    ?
  • d

    dave

    05/14/2021, 11:55 AM
    ok - I'm not sure what the story is with serialisation and 1.5.0 TBH. that's one of the reasons that http4k is still on 1.4.32
  • r

    Razvan

    05/14/2021, 12:18 PM
    Right choice right now, don’t know what it is but since upgrade to 1.5.0 some projects I’ve got a lot of random IntelliJ freezes (even with the workaround the kotlin gradle plugin memory leak). Have to try working on a project not yet upgraded for enough time (or downgrade) to see if it’s that or something else but it happened both on Linux and Mac so kinda suppose it’s related.
  • d

    dave

    11/17/2021, 10:39 AM
    We've released v1.12.2.0 of Forkhandles which is upgrade to Kotlin 1.6 🎉
    🎉 2
  • d

    dave

    12/20/2021, 7:27 PM
    We've released v2.0.0.0 of Forkhandles. The reason for the Version jump is that we've made a breaking change to values4k that might surprise people, but will ultimately enforce the privatisation of the constructor function. It should be very obvious how to apply fixes to your code.
    🎉 1
  • d

    dave

    08/22/2023, 8:34 PM
    🎉 We've just released a new version of Forkhandles. We've upgraded all the dependencies (including Kotlin to 1.9.0), and introduced mock4k: the very cheapest mocking framework platform : See Changelog for details. 🎉
  • a

    Andrew O'Hara

    08/26/2023, 10:26 PM
    @Dmitry Kandalov I was sent here to you by David Denton. Given this parser4k parser, do you know why it can parse
    REMOVE foo
    , but not
    REMOVE foo bar
    ? I thought the
    oneOrMore
    in
    Remove
    would catch multiple space-separated tokens, but I get this error:
    Copy code
    parser4k.InputIsNotConsumed: 
    REMOVE foo bar
              ^
    Copy code
    object DynamoDbUpdateGrammar {
        private val cache = OutputCache<Expr>()
    
        fun parse(expression: String): Expr = expression.parseWith(expr)
    
        private val expr: Parser<Expr> = oneOf(
            //set
            Remove(::expr).with(cache),
            // add
            // delete
        ).reset(cache)
    }
    
    
    private val UpdateAttributeName : ExprFactory = {
        Tokens.identifier.map { name -> Expr { AttributeName.of(name) } }
    }
    
    private val Remove: ExprFactory = { parser ->
        inOrder(
            token("REMOVE"),
            oneOrMore(
                UpdateAttributeName(parser),
            )
        ).skipFirst().map { names ->
            Expr { item ->
                val attributeNames = names.map { it.eval(item) }
                item.item.filterKeys { it !in attributeNames }
            }
        }
    }
  • d

    Dmitry Kandalov

    08/27/2023, 10:07 AM
    Hi @Andrew O'Hara, I think the parser needs
    .joinedWith(" ")
    , where ” ” is the separator between REMOVE arguments. I wasn’t able to run your example directly, so I simplified it a bit 🙈 Not sure why you needed
    ExprFactory
    🤔 Given “REMOVE foo bar”,
    token("REMOVE")
    matches “REMOVE ” (including whitespace),
    oneOrMore(identifier).joinedWith(" ")
    matches “foo” and “bar”.
    Copy code
    import parser4k.OutputCache
    import parser4k.Parser
    import parser4k.commonparsers.Tokens.identifier
    import parser4k.commonparsers.joinedWith
    import parser4k.commonparsers.token
    import parser4k.inOrder
    import parser4k.map
    import parser4k.oneOf
    import parser4k.oneOrMore
    import parser4k.parseWith
    import parser4k.reset
    import parser4k.skipFirst
    import parser4k.with
    
    object DynamoDbUpdateGrammar {
        private val cache = OutputCache<Expr>()
    
        fun parse(expression: String): Expr = expression.parseWith(expr)
    
        private val expr: Parser<Expr> = oneOf(
            Remove.with(cache),
        ).reset(cache)
    }
    
    data class Expr(val id: String)
    
    private val Remove =
        inOrder(
            token("REMOVE"),
            oneOrMore(identifier).joinedWith(" ")
        ).skipFirst().map { names ->
            Expr(names.joinToString())
        }
    
    fun main() {
        println(DynamoDbUpdateGrammar.parse("REMOVE foo bar"))
    }
  • d

    Dmitry Kandalov

    08/27/2023, 2:26 PM
    As an experiment I tried ChatGPT4 🫠 It doesn’t know about parser4k or forkhandles (I guess they need more likes on the Internet). But after copy-pasting readme.md into it one section at a time, it was able to give a correct answer 😱 I don’t mean it as “here is how to use Google”, just curious/impressed that it was able to do it. Here is what ChatGPT says: --------------- The problem you’re facing is that the
    oneOrMore
    parser does capture multiple space-separated tokens, but it expects them to be directly adjacent to one another. In other words, it’s expecting input like
    REMOVE foofoofoo
    instead of
    REMOVE foo bar
    because there’s no parser defined to handle the space between the tokens. To solve this, you can explicitly specify that you’re expecting space-separated tokens. Here’s how you can modify the
    Remove
    parser to handle multiple space-separated tokens:
    Copy code
    private val Remove: ExprFactory = { parser ->
        inOrder(
            token("REMOVE"),
            oneOrMore(
                inOrder(
                    Tokens.space.optional(),
                    UpdateAttributeName(parser)
                ).skipFirst()
            )
        ).skipFirst().map { names ->
            Expr { item ->
                val attributeNames = names.map { it.eval(item) }
                item.item.filterKeys { it !in attributeNames }
            }
        }
    }
    Here’s a breakdown of the changes: 1. Inside the
    oneOrMore
    parser, I wrapped the
    UpdateAttributeName(parser)
    with another
    inOrder
    parser. 2. This new
    inOrder
    parser starts with
    Tokens.space.optional()
    , which tries to parse a space but doesn’t fail if there isn’t one. 3. I used
    .skipFirst()
    to make sure the optional space doesn’t get included in the parsed result. Now,
    Remove
    should be able to parse inputs like
    REMOVE foo bar
    .
  • a

    Andrew O'Hara

    08/27/2023, 3:03 PM
    Right, your explanation makes sense. Adding the optional whitespace token worked. Thanks! Although, I had to use
    Copy code
    optional(Tokens.whitespace)
    rather than
    Copy code
    Tokens.space.optional()
    Is that an artifact of ChatGpt? Or just a pseudocode approximation? I did just make a extension function that provides the
    .optional()
    , so maybe I'll make a small PR for that.
  • a

    Andrew O'Hara

    08/27/2023, 3:08 PM
    Sorry for the lack of a runnable example. It's all very DynamoDB specific and I still lack enough understanding of parser4k to reduce it into a minimal reproducible example. You're right that the
    ExprFactory
    isn't necessary; I was following David's lead from other examples, but I've now been able to remove it from this new parser.
  • d

    Dmitry Kandalov

    08/27/2023, 3:40 PM
    The second message with
    Tokens.space.optional()
    is what ChatGPT generated. As usual, it casually makes things up 🤷 (Technically speaking this is all it does.) Not sure about
    .optional()
    , if there is an extension function for it, then nonRecursive, repeat, zeroOrMore and oneOrMore should probably also have one. On the other hand, don’t remember why/how I decided that
    ref
    deserves an extension function 🤔
  • d

    Dmitry Kandalov

    08/27/2023, 3:41 PM
    In case it helps, there are couple examples here https://github.com/fork-handles/forkhandles/tree/trunk/parser4k/src/test/kotlin/parser4k/examples
  • a

    Andrew O'Hara

    08/27/2023, 3:43 PM
    Yes, the calculator example in particular is what helped me get this far. Before then, I couldn't get anything to parse at all! 😅
  • d

    Dmitry Kandalov

    09/11/2023, 3:24 PM
    In the result4k, I was wondering if
    dev.forkhandles.result4k.Result4k
    should be the default 🤔 Because
    dev.forkhandles.result4k.Result
    will inevitably clash with
    kotlin.Result
    . And since IntelliJ doesn’t keep typealiases when refactoring, the codebase ends up as a mix of
    Result4k
    and
    Result
    .
    a
    d
    n
    • 4
    • 8
  • a

    Andrew O'Hara

    12/06/2023, 8:54 PM
    TIL that values4k supports @JvmInline. I'm curious what kind of benefits one can expect from using it.
    • 1
    • 1
  • d

    dave

    01/05/2024, 5:21 PM
    Hey ForkHandles fans! We've added a new module - data4k - which is inspired by @dmcg’s excellent video series on

    Data-oriented programming▾

    .
    🤔 1
  • d

    dave

    03/24/2024, 1:35 PM
    We recently built a simple state machine module and have been using it successfully on a project . Its also got a rendering engine so you can print out your diagram - which is especially good for explaining biz processes to non tech stakeholders https://github.com/fork-handles/forkhandles/tree/trunk/state4k
    🦜 2
  • n

    natpryce

    04/08/2024, 9:35 PM
    I pushed a commit that adds operators that destructure nullable tuples to nullable elements. E.g. this allows …
    Copy code
    fun maybeReturnsATuple(): Tuple2<String,Int>? = ...
    
    val (a,b) = maybeReturnsATuple()
    Where
    a
    has type
    String?
    and
    b
    has type
    Int?
    .
    👍 1
  • n

    natpryce

    04/08/2024, 9:36 PM
    I wish this was the default behaviour in the language… destructuring and nullability don’t play well together.
    a
    • 2
    • 1
  • a

    Andrew O'Hara

    03/18/2025, 1:41 PM
    I'm curious if there's any thoughts on why you might want to use
    time4k
    over
    java.time.Clock
    on the JVM. I'm not even certain the
    TimeSource
    class will load correctly outside the JVM 🤔 . The best theory I have is that it's simpler to implement a custom
    TimeSource
    than a
    Clock
    .
  • d

    dave

    03/18/2025, 3:25 PM
    Overriding the clock is a pain - especially since Clock is a class and NOT an interface. Really time4k is just a function to get around that annoyance. (Plus of course the DeterministicScheduler which is ace. 🙂
    a
    • 2
    • 2
  • d

    dave

    03/18/2025, 3:26 PM
    Whilst I'm here, we've got a new module in ForkHandles: Fs4k - Super-simple file tree manipulation DSL in Kotlin. https://github.com/fork-handles/forkhandles/tree/trunk/fs4k
    a
    • 2
    • 1