Search code examples
kotlincurrying

Kotlin - how to use closure and currying in kotlin to pass a paramater to returned function with multiple arguments?


I have an enum class that looks like this:

enum class CommandTypes(val access: (Ctx) -> Unit, val command: (Ctx, JsonObject) -> Either<Failure, FlatResp>) {
    CREATE_DELIVERED_EVENT(adminOnly, ::createDeliveredEvent),
    CREATE_SWITCHED_EVENT(adminOnly, ::createSwitchedEvent),
    CREATE_STAMPED_EVENT(adminOnly, ::createStampedEvent),

All of this commands/events have the same logic apart from the type of command/event. They all look like this:

fun createDeliveredEvent(ctx: Ctx, json: JsonObject): Either<Failure, FlatResp> =
    validateAndCreateEvent(ctx, json, EventType.DELIVERED_EVENT)

fun createDeliveredEvent(ctx: Ctx, json: JsonObject): Either<Failure, FlatResp> =
    validateAndCreateEvent(ctx, json, EventType.SWITCHED_EVENT)

fun createDeliveredEvent(ctx: Ctx, json: JsonObject): Either<Failure, FlatResp> =
    validateAndCreateEvent(ctx, json, EventType.STAMPED_EVENT)

Is it possible to pass with currying a type of event to command function, so that I can have a general function that would accept a type and return a function that would have type as an argument. I have tried with something like this:

fun createEvent(type:AirExpressConsignmentEventType): (ctx: Ctx, json: JsonObject) -> Either<Failure, FlatResp> =
{ ctx, json ->  validateAndCreateEvent(ctx, json, type)}

And I am using it like this in the CommandTypes enum class:

CREATE_DELIVERED_EVENT(adminOnly, (::createEvent(EventType.DELIVERED_EVENT)))

But, I get an error:

This syntax is reserved for future use; to call a reference, enclose it in parentheses: (foo::bar)(args)

I am not sure why do I get that, when I already have another piece of code that works with same syntax?

groups = (::infoGroupingWithContext)(ctx)

fun infoGroupingWithContext(ctx: Ctx): (List<Map<String, Any?>>) -> List<Map<String, Any?>> = { rows -> infoGrouping(ctx, rows) }

Solution

  • The syntax for that would be

    fun createEvent(type: EventType): (ctx: Ctx, JsonObject) -> Either<Failure, FlatResp> =
        { ctx, json -> validateAndCreateEvent(ctx, json, type) }
    
    // or perhaps less confusing:
    
    fun createEvent(type: EventType): (ctx: Ctx, JsonObject) -> Either<Failure, FlatResp> {
        return { ctx, json -> validateAndCreateEvent(ctx, json, type) }
    }
    
    // or
    
    fun createEvent(type: EventType) = fun (ctx: Ctx, json: JsonObject): Either<Failure, FlatResp> {
        return validateAndCreateEvent(ctx, json, type)
    }
    

    But perhaps it would be cleaner to do this instead of having to indirectly call that function:

    enum class CommandTypes(
        val access: (Ctx) -> Unit, 
        private val type: EventType
    ) {
        CREATE_DELIVERED_EVENT(adminOnly, EventType.DELIVERED_EVENT),
        CREATE_SWITCHED_EVENT(adminOnly, EventType.SWITCHED_EVENT),
        CREATE_STAMPED_EVENT(adminOnly, EventType.STAMPED_EVENT);
    
        val command: (Ctx, JsonObject) -> Either<Failure, FlatResp>) = {
            validateAndCreateEvent(ctx, json, type)
        }
    }