Arrow Fx + MTL ```override fun open( no: ...
# arrow
s
Arrow Fx + MTL
Copy code
override fun open(
        no: String,
        name: String,
        rate: Option<BigDecimal>,
        openingDate: Option<LocalDate>,
        accountType: AccountType
    ): AccountOperation<Account> = Kleisli.invoke { repo ->
        EitherT(
            repo.query(no).flatMap { accountOptionOrError ->
                accountOptionOrError.fold(
                    { IO { MiscellaneousDomainExceptions(it).left() } },
                    { accOpt ->
                        accOpt.fold(
                            {
                                when (accountType) {
                                    AccountType.CHECKING -> createOrUpdate(
                                        repo,
                                        Account.checkingAccount(no, name, openingDate, None, Balance())
                                    )
                                    AccountType.SAVINGS -> rate.map { r ->
                                        createOrUpdate(
                                            repo,
                                            Account.savingsAccount(no, name, r, openingDate, None, Balance())
                                        )
                                    }.getOrElse { IO { RateMissingForSavingsAccount.left() } }
                                }
                            },
                            { IO { AlreadyExistingAccount(<http://it.no|it.no>).left() } }
                        )
                    }
                )
            }
        )
    }
the future:
Copy code
override suspend fun AccountRepository.open(
        no: String,
        name: String,
        rate: BigDecimal?,
        openingDate: LocalDate?,
        accountType: AccountType
    ): Either<AccountServiceException, Account> = either {
        val existing = query(no).mapLeft { MiscellaneousDomainExceptions(it) }.bind()
        existing.rightIfNull { AlreadyExistingAccount(no) }.bind()

        val accountOrError = when (accountType) {
            AccountType.CHECKING -> Account.checkingAccount(no, name, openingDate, null, Balance())
            AccountType.SAVINGS -> {
                val r = rate.rightIfNotNull { RateMissingForSavingsAccount }.bind()
                Account.savingsAccount(no, name, r, openingDate, null, Balance())
            }
        }
        createOrUpdate(this@open, accountOrError).bind()
    }
the future is bright 😄
😍 5
r
That looks awesome, we are also introducing invoke instead of bind()
any monad can be invoked in its block
s
Interesting 🙂
Btw I removed the Option in favor of Nullable types
r
do you regret it 🧌 ?
s
😂😂 Nope
r
xD
s
There are a few missing functions there... Like Applicative map
1
But nothing that can't be fixed easily
r
I felt the same way the first time I did, Option was the first arrow type and we all loved it.
s
I believe there is a ticket for that
r
yes chaining let is worst than applicative map or tupled
but that syntax is going to be ported as inline implemented with let which also inlines to null checks
so there should be no penalty for using map or tupled over nullables
Also parMap for
suspend -> A?
that shortcircuits on
null
parTraverse etc..
s
Exciting times ahead
I believe this implementation is something that if I explain to people, they can easily understand
Unlike the MTL and Tagless ones
r
right, delimited continuations brings imperative style to FP, so suspend and specialized syntax can make pure FP entirely imperative (also called applicative syntax) that is the effect is applied in place in its delimited continuation scope.
Most developers understand imperative syntax much easier than callback or nested style.