-
Notifications
You must be signed in to change notification settings - Fork 453
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Direct type class syntax
over concrete data types
#811
Conversation
fun <L> Either(): EitherContextPartiallyApplied<L> = | ||
EitherContextPartiallyApplied() | ||
``` | ||
|
||
If you're defining your own instances and would like for them to be discoverable in their corresponding datatypes' companion object, you can generate it by annotating them as `@instance`, and Arrow's [annotation processor](https://github.com/arrow-kt/arrow#additional-setup) will create the extension functions for you. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This block belongs to the Instances section, before the Syntax one.
syntax
over concrete data types
} | ||
|
||
fun <L> Either(): EitherContextPartiallyApplied<L> = | ||
EitherContextPartiallyApplied() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we hide the partial implementation?
interface ContextPartiallyApplied<T> {
infix fun <A> syntax(f: T.() -> A): A
}
fun <L> Either() =
object: ContextPartiallyApplied<EitherContext<L>> {
infix fun <A> syntax(f: EitherContext<L>.() -> A): A =
f(EitherContext())
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yep
To align with the existing documentation I'd rather the DSL objects were suffixed |
Another suggestion I can think of is to move the functions to the companion object of the witness types. That way we can get consistent and unambiguous syntax across types, i.e. |
@pakoito Many types use now String syntax {
"a".combine("b")
}
// ab |
Here's a snippet that reflects my proposal: Definition: object ListKSyntax : Syntax<ListKSyntax>, ListKMonadInstance, ListKTraverseInstance, ListKMonoidKInstance {
override fun <A, B> Kind<ForListK, A>.map(f: (A) -> B): ListK<B> =
fix().map(f)
override infix fun <A> syntax (f: ListKSyntax.() -> A): A =
f(ListKContext)
}
operator fun <A> ForListK.Companion.invoke(): Syntax<ListKSyntax> =
ListKSyntax Usage: ForOption() syntax {
just(1).sequence(ForListK())
} For platform types like String or Boolean we will create a new object prefixed with arrow/modules/core/arrow-instances-core/src/main/kotlin/arrow/instances/boolean.kt Lines 22 to 29 in 1020e06
|
Proposal to change import arrow.instances.*
Option instances { //`this` is Monad, Applicative, Functor, Traverse, etc...
binding { ... }
map(....)
traverse(...)
}
|
What about |
I'm a little unsure about |
@nomisRev that has access to the Effect instance. ForObservableK instances {
// `this` is also an `Effect<ForObservableK>`
} https://github.com/arrow-kt/arrow/pull/811/files#diff-088c779bf125135ce8dbe42b7af6d477R119 |
Ah yeah of course. That data type only lives in effects... My bad :D I meant if I manually add a instance. Or typeclasses/instances defined in Optics, Helios,.. |
@nomisRev For those cases we are providing instructions to users as part of this PR that tells them how to create the same DSL including their custom instances: |
@pakoito @ersin-ertan @nomisRev Does this look ok to everyone and a fair compromise for now? ForOption extensions { ... }
ForIO extensions { ... }
ForBoolean extensions { ... }
ForEither<String>() extensions { ... } |
Looks good to me! Also thanks for pointing out the entry in docs 👍 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good. Clever re-usage of For... too
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For... extensions gets my green!
* `Const.run(String.monoid) { ... }` + `with(Const, String.monoid()) { ... }` * `Either.run { ... }` + `with(Either) { ... }` * `Eval.run { ... }` + `with(Eval) { ... }` * `Function0.run { ... }` + `with(Function0) { ... }` * `Function1.run<I> { ... }` + `with<I>(Function1) { ... }` * `Id.run { ... }` + `with(Id) { ... }` * `Byte.run { ... }` + `with(Byte) { ... }` * `Double.run { ... }` + `with(Double) { ... }` * `Int.run { ... }` + `with(Int) { ... }` * `Long.run { ... }` + `with(Long) { ... }` * `Short.run { ... }` + `with(Short) { ... }` * `Float.run { ... }` + `with(Float) { ... }` * `Option.run { ... }` + `with(Option) { ... }` * `Either<L>().run { ... }` + `with(Either<L>()) { ... }` * `Const(String.monoid()).run { ... }` + `with(Const(String.monoid())) { ... }` * `Function1<I>().run { ... }` + `with(Function1<I>()) { ... }` * `String.run { ... }` + `with(String) { ... }` * `Try.run { ... }` + `with(Try) { ... }` * `Coproduct(TF, TG).run { ... }` + `with(Coproduct(TF, TG)) { ... }` * `EitherT(MF).run { ... }` + `with(EitherT(MF)) { ... }` * `Ior(SL).run { ... }` + `with(Ior(SL)) { ... }` * `Kleisli(MF).run { ... }` + `with(Kleisli(MF)) { ... }` * `ListK.run { ... }` + `with(ListK) { ... }` * `MapK<Key>().run { ... }` + `with(MapK<Key>()) { ... }` * `NonEmptyList.run { ... }` + `with(NonEmptyList) { ... }` * `OptionT(MF).run { ... }` + `with(OptionT(MF)) { ... }` * `SequenceK.run { ... }` + `with(SequenceK) { ... }` * `SetK.run { ... }` + `with(SetK) { ... }` * `SortedMapK<Key>().run { ... }` + `with(SortedMapK<Key>()) { ... }` * `StateT(ME).run { ... }` + `with(StateT(ME)) { ... }` * `Validated(SL).run { ... }` + `with(Validated(SL)) { ... }` * EitherT type arg name fixes * `WriterT(MF, MW).run { ... }` + `with(WriterT(MF, MW)) { ... }` * mtl: `Const(MA).run { ... }` + `with(Const(MA)) { ... }` * mtl: `Function1<I>().run { ... }` + `with(Function1<I>()) { ... }` * mtl: `Kleisli<F, D, E>(ME).run { ... }` + `with(Kleisli<F, D, E>(ME)) { ... }` * mtl: `ListK.run { ... }` + `with(ListK) { ... }` * mtl: `Option.run { ... }` + `with(Option) { ... }` * mtl: `OptionT(MF).run { ... }` + `with(OptionT(MF)) { ... }` * mtl: `StateT(ME).run { ... }` + `with(StateT(ME)) { ... }` * mtl: `WriterT(MF, MW).run { ... }` + `with(WriterT(MF, MW)) { ... }` * mtl: `DeferredK.run { ... }` + `with(DeferredK) { ... }` * mtl: `FlowableK.run { ... }` + `with(FlowableK) { ... }` * mtl: `ObservableK.run { ... }` + `with(ObservableK) { ... }` * mtl: `IO.run { ... }` + `with(IO) { ... }` * mtl: `Fix(FF).run { ... }` + `with(Fix(FF)) { ... }` * Replaced `run` with infix `syntax` * Ank docs for `Either` and `EitherT` using `syntax` DSL * Ank docs for `NonEmptyList` using `syntax` DSL * Ank docs for `Option` using `syntax` DSL * Ank docs for `Option` and `OptionT` using `syntax` DSL * Ank docs for `SequenceK` using `syntax` DSL * Ank docs for `State` using `syntax` DSL * Ank docs for `StateT` using `syntax` DSL * Ank docs for `Try` using `syntax` DSL * Ank docs for `IO` using `syntax` DSL * Ank docs for `@product` using `syntax` DSL * Ank docs for `DeferredK` using `syntax` DSL * Ank docs for `ObservableK` using `syntax` DSL * Ank docs for DI using `syntax` DSL * Ank docs for Error Handling using `syntax` DSL * Ank docs for Glossary using `syntax` DSL * Ank docs for `MonadError` using `syntax` DSL * Ank docs for `MonadFilter` using `syntax` DSL * Ank docs for `Monoid` using `syntax` DSL * Ank docs for `Order` using `syntax` DSL * Ank docs for `Semigroup` using `syntax` DSL * Ank docs for `Show` using `syntax` DSL * Progress toward refactoring tests * data types syntax in `arrow-free` and related tests * code review * `syntax` -> `extensions` * `For` prefix
The following PR adds a
syntax
DSL for all data types that arrow exposes type class instances for.This DSL allows users to not worry about knowing if
binding
,flatMap
etc are inMonad
, or what any other combinator they plan on using belongs to a certain type class.It eliminates the need to explicitly refer to the instances inside the data type companion.
Until now users where forced to do things like:
Instead with the syntax DSL most used type classes that the data type provides instances for are bundled as the single
this
scope of the containedsyntax
function.The scope contains all extension functions and static functions that the type classes define and are available to use within the user provided block.
The docs contain now instructions for users wanting to provide a similar DSL for their custom data type and it looks like this: