addamsson
07/14/2019, 11:07 AMraulraja
07/14/2019, 11:44 AMsimon.vergauwen
07/14/2019, 12:12 PMfor Arrow to become MPP we are developing typeclasses of KEEP-87 as a compiler pluginIs this blocking core to be MPP?
Monad#fx
and @higherkind
seem the blockers to me. @higherkind
could temporarly be replaced by its boilerplate.
If we can find a (temp) solution to seperate Monad#fx
that would open the door to make core MPP already.addamsson
07/14/2019, 12:14 PMsimon.vergauwen
07/14/2019, 12:16 PMaddamsson
07/14/2019, 12:16 PMsimon.vergauwen
07/14/2019, 12:37 PMaddamsson
07/14/2019, 12:37 PMsimon.vergauwen
07/14/2019, 12:38 PMaddamsson
07/14/2019, 12:38 PMsimon.vergauwen
07/14/2019, 12:39 PMaddamsson
07/14/2019, 12:39 PMsimon.vergauwen
07/14/2019, 12:39 PMaddamsson
07/14/2019, 12:39 PMsimon.vergauwen
07/14/2019, 12:40 PMaddamsson
07/14/2019, 12:40 PMsimon.vergauwen
07/14/2019, 12:41 PMfor
comphresions/do-notation, for perf and MPP reasons we’ll replace this with compiler plugins.addamsson
07/14/2019, 12:41 PMsimon.vergauwen
07/14/2019, 12:41 PMaddamsson
07/14/2019, 12:42 PMsimon.vergauwen
07/14/2019, 12:42 PMaddamsson
07/14/2019, 2:07 PMValue
(which they realized they need to remove)Iterable
Option
which is an empty Iterable
if it is None
and an Iterable
with 1 element if it is Some
Either
?simon.vergauwen
07/14/2019, 2:13 PMIterable
is the wrong abstraction here. fold
is what you want to use instead, it's abstracted over by Foldable
. Which is the polymorphic way of providing this API.addamsson
07/14/2019, 2:16 PMsimon.vergauwen
07/14/2019, 2:32 PMMonad
(flatMap), `Foldable`(fold), Traverse
(traverse), FunctorFilter
(filter) etcaddamsson
07/14/2019, 2:40 PMIterable
with these?myMap.toPersistentMap().map { (key, value) -> ...}.fold { ... }
Iterable
so I need to supply specializations for all of them which work with `PersistentCollection`ssimon.vergauwen
07/14/2019, 2:56 PMIterable
on your persistent collections. This way you can avoid getting tangled into the extensions functions of the std.object PersistedMapFunctor
available is enough for the compiler to know map
exists for PersistentMap
.addamsson
07/14/2019, 3:18 PMThis way you can avoid getting tangled into the extensions functions of the std.
This is what I implied, yes!arrow
./gradlew clean build
resulted in a wall of text of compilation errors. Am I missing something? A switch maybe?D:\dev\forks\arrow\modules\validation\arrow-validation\build\generated\source\kaptKotlin\main\extension\arrow\validation\refinedTypes\numeric\either\nonZero\EitherNonZero.kt: (18, 47): Type argument is not within its bounds: should be subtype of 'Number'
raulraja
07/14/2019, 9:29 PMaddamsson
07/15/2019, 1:07 PMraulraja
07/15/2019, 3:16 PMaddamsson
07/15/2019, 4:12 PM./gradlew clean build
takes 32m?pakoito
07/15/2019, 4:15 PMaddamsson
07/15/2019, 4:16 PMPersistentMap
using Arrow with the plain old JVM target (no MPP) just to see how it works, although I have one question:raulraja
07/15/2019, 5:51 PMremove
?addamsson
07/15/2019, 5:51 PMfun remove(element: E): PersistentMap<E>
raulraja
07/15/2019, 5:51 PMaddamsson
07/15/2019, 5:55 PMelement
FunctorFilter
raulraja
07/15/2019, 5:58 PMaddamsson
07/15/2019, 5:58 PMraulraja
07/15/2019, 5:58 PMremove
operation to wherever it fitsaddamsson
07/15/2019, 6:00 PMfilter
?remove
is a specialized version of filter
list.filter { it != element}
O(n)
vs O(1)
raulraja
07/15/2019, 6:02 PMaddamsson
07/15/2019, 6:02 PMeq
for E
and which has filter
FunctorFilter
and Eq
remove
to FunctorFilter
and have the default implementation which is O(n)
O(1)
version for PersistentMap
raulraja
07/15/2019, 6:04 PMfun <F, A> Kind<F, A>.remove(a: A): Kind<F, A> =
if (this.contains(a)) this.filter { it == a }
else this
addamsson
07/15/2019, 6:04 PMEq
comes into the pictureraulraja
07/15/2019, 6:04 PMaddamsson
07/15/2019, 6:05 PMKind<F, A>
Kind<ForPeristentMap, A>
?simon.vergauwen
07/15/2019, 6:10 PMO(n)
impl would exist in FunctorFilter
and a fast impl would override the default in FunctorFilter<PersistentMapOf<Keys>>
and FunctorFilter<ForPersistentSet>
raulraja
07/15/2019, 6:11 PMaddamsson
07/15/2019, 6:11 PMraulraja
07/15/2019, 6:12 PMfun <A> Kind<F, A>.remove(a: A): Kind<F, A> =
mapFilter {
if (a == it) Some(it)
else None
}
==
is where the equality plays a roleremoveIf
etc…simon.vergauwen
07/15/2019, 6:15 PMfold
code insteadraulraja
07/15/2019, 6:16 PMcontains
etc.pakoito
07/15/2019, 6:17 PMif (a == it) Some(it)
==> one thing to mention, all maps in Haskell use Eq and Ord for keysaddamsson
07/15/2019, 6:22 PMFoldable
seems like a reasonable choice if we care about utilityFunctorFilter
Eq
should be used instead of ==
Eq
arrow-core-data
?core
concept?simon.vergauwen
07/15/2019, 6:28 PMcore
not perse, if the module is small enough I'd say yes.raulraja
07/15/2019, 6:34 PMremove
would go in FunctorFilter or Foldable in core and the actual collection implementations datatypes and extensions a arrow-persistent-collections
or similar named moduleaddamsson
07/15/2019, 7:55 PMFunctorFilter
or Foldable
?PersistentMap
on a branch in my Arrow fork and open a PR you can reviewPersistentSet
+ PersistentList
together with PersistentMap
in one go)pakoito
07/15/2019, 8:08 PM1. I’ll finish my port of persistent data structures to Kotlin in my own projectwhat’s missing/necessary?
addamsson
07/15/2019, 8:10 PMcommon
project so retrofitting to MPP will be a no-op 😄pakoito
07/15/2019, 8:18 PMthen I have to refactor the type hierarchy to align it with ArrowI’m curious about this, if the code is shared through inheritance
addamsson
07/15/2019, 8:20 PMpakoito
07/15/2019, 8:38 PMaddamsson
07/15/2019, 8:39 PMnull
keys should be allowed?pakoito
07/15/2019, 9:08 PMaddamsson
07/15/2019, 9:10 PMraulraja
07/15/2019, 11:12 PMaddamsson
07/16/2019, 7:10 AM<K, V>
so null
is theoretically acceptedinterface
for HAMT right now:interface HashArrayMappedTrie<K, V> : Iterable<Tuple2<K, V>> {
val isEmpty: Boolean
val size: Int
operator fun get(key: K): Option<out V>
fun containsKey(key: K): Boolean
fun put(key: K, value: V): HashArrayMappedTrie<K, V>
fun remove(key: K): HashArrayMappedTrie<K, V>
fun keysIterator(): Iterator<K>
fun valuesIterator(): Iterator<V>
}
Option
instead of the VAVR oneraulraja
07/16/2019, 12:28 PMaddamsson
07/16/2019, 12:56 PMMapK
core/arrow-persistent-collections
as a module for the implementations?raulraja
07/17/2019, 11:09 AMaddamsson
07/17/2019, 11:09 AMmaster
?raulraja
07/17/2019, 11:10 AMaddamsson
07/17/2019, 11:12 AMarrow-persistent-collections
in the modules
folder, I'll branch from master
and I'll augment Foldable
. If that's not gonna work I'll use FunctorFilter
MapK
as an examplepakoito
07/17/2019, 11:13 AMaddamsson
07/17/2019, 11:13 AMk()
function btw which creates a ListK
form a List
?pakoito
07/17/2019, 11:13 AMk()
is how you wrap stuffaddamsson
07/17/2019, 11:14 AMk
and the prefix K
means?pakoito
07/17/2019, 11:14 AMaddamsson
07/17/2019, 11:15 AMpakoito
07/17/2019, 11:15 AMraulraja
07/17/2019, 11:17 AMaddamsson
07/17/2019, 11:18 AMraulraja
07/17/2019, 11:18 AMaddamsson
07/17/2019, 11:18 AMraulraja
07/17/2019, 11:18 AMaddamsson
07/17/2019, 11:18 AMraulraja
07/17/2019, 11:19 AMaddamsson
07/17/2019, 11:19 AMraulraja
07/17/2019, 11:19 AMaddamsson
07/17/2019, 11:19 AMraulraja
07/17/2019, 11:20 AMaddamsson
07/17/2019, 11:20 AMraulraja
07/17/2019, 11:20 AMaddamsson
07/17/2019, 11:21 AMsimon.vergauwen
07/17/2019, 11:23 AMraulraja
07/17/2019, 11:25 AMaddamsson
07/17/2019, 11:29 AMsimon.vergauwen
07/17/2019, 11:30 AMaddamsson
07/17/2019, 11:30 AMsimon.vergauwen
07/17/2019, 11:30 AMraulraja
07/17/2019, 11:32 AMaddamsson
07/17/2019, 11:32 AMraulraja
07/17/2019, 11:32 AMaddamsson
07/17/2019, 11:34 AMraulraja
07/17/2019, 11:35 AMaddamsson
07/17/2019, 11:35 AMraulraja
07/17/2019, 11:36 AMaddamsson
07/17/2019, 11:36 AMraulraja
07/17/2019, 11:36 AMaddamsson
07/17/2019, 11:36 AMraulraja
07/17/2019, 11:38 AMaddamsson
07/17/2019, 11:38 AMraulraja
07/17/2019, 11:38 AMaddamsson
07/17/2019, 11:38 AMraulraja
07/17/2019, 11:41 AMaddamsson
07/17/2019, 11:45 AMraulraja
07/17/2019, 11:46 AMaddamsson
07/17/2019, 11:47 AMMapK
Map
in our PersistentMap
Map
which just don't make sense in our implementationmodules/persistent/arrow-persistent-data-structures
because it is better aligned with the naming I see thereraulraja
07/18/2019, 11:03 AMaddamsson
07/18/2019, 11:32 AMTuple2
?@extension
interface PersistentMapFoldable<K, V> : Foldable<Tuple2<K, V>> {
override fun <A, B> Kind<Tuple2<K, V>, A>.foldLeft(b: B, f: (B, A) -> B): B {
TODO("not implemented")
}
override fun <A, B> Kind<Tuple2<K, V>, A>.foldRight(lb: Eval<B>, f: (A, Eval<B>) -> Eval<B>): Eval<B> {
TODO("not implemented")
}
}
MapK
uses the partially applied type and only folds keyswith(intFold) {
"Folding a list of ints" {
forAll(Gen.list(<http://Gen.int|Gen.int>())) { ints ->
fold(Int.monoid(), ints.k()) == ints.sum()
}
}
}
raulraja
07/18/2019, 11:51 AMaddamsson
07/18/2019, 11:51 AM@JvmName("orEmpty")
@Suppress(
"UNCHECKED_CAST",
"USELESS_CAST",
"EXTENSION_SHADOWED_BY_MEMBER",
"UNUSED_PARAMETER"
)
fun <K, V, A> orEmpty(arg0: Applicative<Tuple2<K, V>>, arg1: Monoid<A>): Tuple2<K, V, A> =
arrow.core.Tuple2
.foldable<K, V>()
.orEmpty<A>(arg0, arg1) as arrow.core.Tuple2<K, V, A>
raulraja
07/18/2019, 11:51 AMaddamsson
07/18/2019, 11:52 AMraulraja
07/18/2019, 11:52 AMaddamsson
07/18/2019, 11:53 AMraulraja
07/18/2019, 11:53 AMaddamsson
07/18/2019, 11:53 AMFoldable<Tuple2<K, V>>
is not workingraulraja
07/18/2019, 12:00 PMaddamsson
07/18/2019, 12:05 PMforAll {}
constuct is usedraulraja
07/18/2019, 12:06 PM==
or Eq
usually is the wayaddamsson
07/18/2019, 12:07 PM"Should be able to use fold as a filter" {
val result = listOf(1, 2, 3, 4).k().foldLeft(listOf<Int>()) { list, next ->
if (next == 2) {
list
} else list.plus(next)
}
with(result) {
forAll {
result == listOf(0, 3, 4)
}
}
}
result
is list(1, 3, 4)
FoldTest
Foldable
is sufficient to implement remove
what we've been talking about yesterdaysimon.vergauwen
07/18/2019, 12:13 PMforAll
passed but not sure how it work without arguments.with(result)
, to result shouldBe listOf(0, 3, 4)
. Does it fail then?addamsson
07/18/2019, 12:18 PMFoldable
is not a Monoid
we don't have compose
identity
fun <A> Kind<F, A>.remove(element: A): Kind<F, A> = foldLeft(???) { foldable, next ->
if(element == next) {
foldable
} else foldable ??? next
}
simon.vergauwen
07/18/2019, 12:41 PMfun <A> Kind<F, A>.remove(element: A, MA: Monoid<A>): Kind<F, A>
in Foldable
.fun <A> Kind<F, A>.remove(element: A): Kind<F, A>
can only live in FunctorFilter
.addamsson
07/18/2019, 12:42 PMfun <A> Kind<F, A>.remove(element: A, monoid: Monoid<A>): Kind<F, A> = foldLeft(monoid.empty()) { foldable, next ->
if(element == next) {
foldable
} else monoid.???
}
simon.vergauwen
07/18/2019, 12:46 PMaddamsson
07/18/2019, 12:46 PMsimon.vergauwen
07/18/2019, 12:47 PMaddamsson
07/18/2019, 12:47 PMsimon.vergauwen
07/18/2019, 12:47 PMMonoidK
for F
.addamsson
07/18/2019, 12:47 PMsimon.vergauwen
07/18/2019, 12:48 PMFunctorFilter
.addamsson
07/18/2019, 12:49 PM0.2%
simon.vergauwen
07/18/2019, 12:52 PMremove
with filter
makes more sense to me than grouping it with fold
.addamsson
07/18/2019, 12:52 PMcombine
is an extension functionadd
the next element to itfun <A> Kind<F, A>.remove(element: A, MF: MonoidK<F>, MA: Monoid<A>): Kind<F, A> = MF.run {
foldLeft(empty()) { acc, a ->
if (a == element) {
acc
} else ???
}
}
Monoid<MonoidK<F>>
?simon.vergauwen
07/18/2019, 12:58 PMfun <A> Kind<F, A>.remove(element: A,
monoid: Monoid<A>,
monoidK: MonoidK<F>,
AF: Applicative<F>): Kind<F, A> = AF.run {
foldLeft(AF.just(monoid.empty())) { foldable: Kind<F, A>, next: A ->
if (next == element) foldable
else monoidK.run { foldable.combineK(AF.just(next)) }
}
}
addamsson
07/18/2019, 12:59 PMApplicative
😞simon.vergauwen
07/18/2019, 12:59 PMMonoidK
is what you’re looking for, it’s meant to combine `F`s, * -> *
or constructors with a typehole.monoid.empty()
returns A
but the return type of our foldLeft
needs to be Kind<F, A>
so we need to lift it.addamsson
07/18/2019, 12:59 PMApplicative
providessimon.vergauwen
07/18/2019, 1:00 PMjust
which can (A) -> Kind<F, A>
addamsson
07/18/2019, 1:01 PMApplicative
works? I'm kinda lost heresimon.vergauwen
07/18/2019, 1:01 PMA
into F
, and then we can use MonoidK
to combine F
. You can overload this function in Traverse
and default supply the Applicative
instance.addamsson
07/18/2019, 1:01 PMsimon.vergauwen
07/18/2019, 1:02 PMfun <A> Kind<F, A>.remove(element: A,
monoid: Monoid<A>,
monoidK: MonoidK<F>,
AF: Applicative<F>): Kind<F, A> {
val emptyValue: A = monoid.empty()
val liftedEmpty: Kind<F, A> = AF.just(emptyValue)
return foldLeft(liftedEmpty) { foldable: Kind<F, A>, next: A ->
if (next == element) foldable
else monoidK.run {
val liftedNext = AF.just(next)
val newlyAccumulated = foldable.combineK(liftedNext)
newlyAccumulated
}
}
}
addamsson
07/18/2019, 1:03 PMjust
does is it wraps a value in a context?simon.vergauwen
07/18/2019, 1:03 PMfun <A> just(a: A): List<A> = listOf(a)
addamsson
07/18/2019, 1:03 PMsimon.vergauwen
07/18/2019, 1:03 PMaddamsson
07/18/2019, 1:03 PMOption.just(1)
simon.vergauwen
07/18/2019, 1:04 PMF
so we can accumulate the results in our collections. MonoidK.combineK
is append or +
for List
.addamsson
07/18/2019, 1:05 PMnext
values?listOf()
+ listOf(1)
+ listOf(3)
+ listOf(4)
?simon.vergauwen
07/18/2019, 1:06 PMaddamsson
07/18/2019, 1:07 PMsimon.vergauwen
07/18/2019, 1:09 PMfoldLeft
over our original Kind<F, A>
, and as an initial value we pass an “empty context”.
I thought the empty context could be constructed by using Monoid
+ Applicative
, but that’s inaccurate.
Monoid<Int>
returns 0
on empty
so wrapping it with Applicative#just
results in listOf(0)
instead of emptyList()
So what we want instead is simply MonoidK
, which it’s empty
returns emptyList()
.addamsson
07/18/2019, 1:09 PMmonoidK
has empty
simon.vergauwen
07/18/2019, 1:09 PMMonoid
either.addamsson
07/18/2019, 1:10 PMfun <A> Kind<F, A>.remove(element: A,
monoidK: MonoidK<F>,
AF: Applicative<F>): Kind<F, A> {
val emptyValue = monoidK.run {
empty<A>()
}
return foldLeft(emptyValue) { foldable: Kind<F, A>, next: A ->
if (next == element) foldable
else monoidK.run {
foldable.combineK(AF.just(next))
}
}
}
simon.vergauwen
07/18/2019, 1:10 PMaddamsson
07/18/2019, 1:11 PMsimon.vergauwen
07/18/2019, 1:11 PMval emptyValue = monoidK.run {
empty<A>()
}
Can simply be monoidK.empty<A>()
emptyList() + listOf(1) + listOf(3) + listOf(4)
for ListK.foldable().run { listOf(1, 2, 3, 4).k().remove(2) }
emptyList().combineK(listOf(1).combineK(listOf(3).combineK(listOf(4))))
=> monoidK.empty().combineK(just(1).combineK(just(3).combineK(just(4))))
addamsson
07/18/2019, 1:15 PMApplicative
is?Applicative
is for yanking?simon.vergauwen
07/18/2019, 1:18 PMApplicative
😅 In Arrow it only adds the power to lift values into F
on top of Functor
.addamsson
07/18/2019, 1:18 PM"Should be able to remove from Foldable" {
val result = ListK.foldable().run { listOf(1, 2, 3, 4).k().remove(2, ListK.monoidK(), ListK.applicative()) }
result shouldBe listOf(1, 3, 4)
}
just
Eq
instead of ==
?simon.vergauwen
07/18/2019, 1:23 PMap
, which is not that useful in Kotlin and split off in Apply
. What is more interesting there is the derived functions which allow for combining N types wrapped in F
.
ListK.applicative(listOf(1, 2), listOf(3, 4)) { (a, b) -> Tuple2(a, b) }
//ListK(list=[Tuple2(a=1, b=3), Tuple2(a=1, b=4), Tuple2(a=2, b=3), Tuple2(a=2, b=4)])
Eq
instead of ==
but most of the tests that aren’t using Eq
have been there for a long time. We haven’t actively gone back to tests to rewrite them.addamsson
07/18/2019, 1:25 PMsimon.vergauwen
07/18/2019, 1:28 PMaddamsson
07/18/2019, 1:28 PMsimon.vergauwen
07/18/2019, 1:28 PMaddamsson
07/18/2019, 1:29 PMsimon.vergauwen
07/18/2019, 1:32 PMF
and turns them into a tuple.
fun <F, A, B> Applicative<F>.tupled(fa: Kind<F, A>, fb: Kind<F, B>) : Kind<F, Tuple2<A, B>> {
val ff: Kind<F, (B) -> Tuple2<A, B> = fa.map { a -> { b -> Tuple2(a, b) } }
return fb.ap(ff)
}
addamsson
07/18/2019, 1:32 PMlistOf(1, 2, 3, 4).k().remove(2, ListK.monoidK(), ListK.applicative())
simon.vergauwen
07/18/2019, 1:32 PMMonad
and flatMap
but that means it’ll always be sequential.addamsson
07/18/2019, 1:33 PMsimon.vergauwen
07/18/2019, 1:33 PMIO
we could write an Applicative
that uses parMapN
to combine 2 values in parallel.addamsson
07/18/2019, 1:34 PMsimon.vergauwen
07/18/2019, 1:34 PMFunctorFilter
alternativeConcurrent
typeclass for a parApplicative()
instance.Applicative
in a concurrent/parallel manner with guarantees about cancelation, error handling and resource safety etc.addamsson
07/18/2019, 1:36 PMApplicative
is capable of doing thatsimon.vergauwen
07/18/2019, 1:37 PMaddamsson
07/18/2019, 1:38 PMApply
has no kdocs 😢simon.vergauwen
07/18/2019, 1:39 PMaddamsson
07/18/2019, 1:40 PMfun <A, B> Kind<F, A>.ap(ff: Kind<F, (A) -> B>): Kind<F, B>
simon.vergauwen
07/18/2019, 1:40 PMaddamsson
07/18/2019, 1:40 PMApply
does is that it applies a function wrapped in a context to a value wrapped in a contextsimon.vergauwen
07/18/2019, 1:41 PMaddamsson
07/18/2019, 1:42 PMsimon.vergauwen
07/18/2019, 1:42 PMaddamsson
07/18/2019, 1:43 PMsimon.vergauwen
07/18/2019, 1:44 PMaddamsson
07/18/2019, 1:48 PMsimon.vergauwen
07/18/2019, 1:49 PMaddamsson
07/18/2019, 1:49 PMsimon.vergauwen
07/18/2019, 1:51 PMaddamsson
07/18/2019, 1:51 PMEq
into this implementation of remove
?simon.vergauwen
07/18/2019, 1:54 PMEq.any()
is ==
.
fun <A> Kind<F, A>.remove(element: A,
monoidK: MonoidK<F>,
AF: Applicative<F>,
eq: Eq<A> = Eq.any()): Kind<F, A> =
foldLeft(monoidK.empty()) { foldable: Kind<F, A>, next: A ->
if (eq.run { next.eqv(element) }) foldable
else monoidK.run { foldable.combineK(AF.just(next)) }
}
cc\ @raulraja we need KEEP-87 xDaddamsson
07/18/2019, 1:55 PMraulraja
07/18/2019, 1:58 PMval MetaComponentRegistrar.typeClasses: List<ExtensionPhase>
get() =
meta(
classOrObject(::isExtension) { ktClass ->
val typeClass = ktClass.typeClassName()
val typeArgs = ktClass.typeArgumentNames()
val factoryName = typeClass.decapitalize()
println("intercepted ${ktClass.name}")
listOf(
ktClass.text,
"fun $factoryName(): $typeClass<${typeArgs..","}> = TODO()"
)
},
IrGeneration { compilerContext, file, backendContext, bindingContext ->
file.transformChildren(object: IrElementTransformer<Unit> {
}, Unit)
}
)
typeArgs..","
","
can be any other delimitersimon.vergauwen
07/18/2019, 2:00 PMraulraja
07/18/2019, 2:00 PMsimon.vergauwen
07/18/2019, 2:00 PMraulraja
07/18/2019, 2:01 PMprivate operator fun <A> List<A>.rangeTo(s: String): String =
joinToString(s)
addamsson
07/18/2019, 2:15 PMfun <A> Kind<ForListK, A>.remove(element: A) = remove(element, ListK.monoidK(), ListK.applicative())
@extension
interface ListKFoldable : Foldable<ForListK> {
override fun <A, B> Kind<ForListK, A>.foldLeft(b: B, f: (B, A) -> B): B =
fix().foldLeft(b, f)
override fun <A, B> Kind<ForListK, A>.foldRight(lb: Eval<B>, f: (A, Eval<B>) -> Eval<B>): Eval<B> =
fix().foldRight(lb, f)
override fun <A> Kind<ForListK, A>.isEmpty(): kotlin.Boolean =
fix().isEmpty()
fun <A> Kind<ForListK, A>.remove(element: A) = remove(element, ListK.monoidK(), ListK.applicative())
}
remove
to Foldable
I'm going to augment all implementations with this to keep it ergonomicsimon.vergauwen
07/18/2019, 2:16 PMListKFoldable
interface.addamsson
07/18/2019, 2:17 PM"Should be able to remove from Foldable" {
val result = ListK.foldable().run { listOf(1, 2, 3, 4).k().remove(2) }
println(result)
result shouldBe listOf(1, 3, 4)
}
Foldable
@extension
will create an unboxed extension functionsimon.vergauwen
07/18/2019, 2:19 PMaddamsson
07/18/2019, 2:20 PMListK
then, thankssimon.vergauwen
07/18/2019, 2:20 PMaddamsson
07/18/2019, 2:21 PMremove
would make ListK
invariant 😞<out A>
simon.vergauwen
07/18/2019, 2:22 PMListK
it is. The only way to walk around that problem now since Kotlin doesn’t allow for variance in functions.pakoito
07/18/2019, 2:23 PMsimon.vergauwen
07/18/2019, 2:23 PMaddamsson
07/18/2019, 2:32 PMremove
function we imlpementedfun <A> ListK<A>.remove(element: A): ListK<A> = this.??? // can't find it
fix()
doesn't help eitherListK
itselfFoldable
functions are implemented in ListKFoldable
?simon.vergauwen
07/18/2019, 2:34 PMimport arrow.core.extensions.listk.foldable.remove
addamsson
07/18/2019, 2:36 PMsimon.vergauwen
07/18/2019, 2:36 PMaddamsson
07/18/2019, 2:36 PMremove
for ListK
we could just use the wrapped `List`'s remove
simon.vergauwen
07/18/2019, 2:37 PMaddamsson
07/18/2019, 2:37 PM"Should be able to remove from Foldable" {
val result = ListK.foldable()
.run { listOf(1, 2, 3, 4).k() }
.remove(2, ListK.monoidK(), ListK.applicative())
result shouldBe listOf(1, 3, 4)
}
Foldable
thoughsimon.vergauwen
07/18/2019, 2:39 PMarrow-test
they’re implemented as an abstract test suite. Called FoldableLaws.laws(...)
addamsson
07/18/2019, 2:39 PMsimon.vergauwen
07/18/2019, 2:39 PMaddamsson
07/18/2019, 2:40 PMPersistentMap
implementation?/**
* A [PersistentMap] is an immutable [Map] which implements structural sharing.
*/
@higherkind
data class PersistentMap<K, V>(private val map: Map<K, V>) : PersistentMapOf<K, V> {
val isEmpty: Boolean = map.isEmpty()
val size: Int = map.size
operator fun get(key: K): Option<V> = Option.fromNullable(map[key])
fun getOrElse(key: K, defaultValue: V): V = map.getOrElse(key) { defaultValue }
fun containsKey(key: K): Boolean = map.containsKey(key)
fun put(key: K, value: V): PersistentMap<K, V> {
TODO()
}
fun remove(key: K): PersistentMap<K, V> {
TODO()
}
}
pakoito
07/18/2019, 2:54 PMaddamsson
07/18/2019, 3:05 PM/**
* A [PersistentMap] is an immutable [Map] which implements structural sharing.
*/
@higherkind
data class PersistentMap<K, V>(private val map: HashArrayMappedTrie<K, V>) : PersistentMapOf<K, V> {
val isEmpty: Boolean = map.isEmpty
val size: Int = map.size
operator fun get(key: K): Option<V> = map[key]
fun containsKey(key: K): Boolean = map.containsKey(key)
fun put(key: K, value: V): PersistentMap<K, V> {
return PersistentMap(map.put(key, value))
}
fun remove(key: K): PersistentMap<K, V> {
return PersistentMap(map.remove(key))
}
}
pakoito
07/18/2019, 3:55 PMaddamsson
07/18/2019, 4:18 PMpakoito
07/18/2019, 4:20 PMaddamsson
07/18/2019, 4:22 PMMapK
are necessary because we don't have KEEP-87 yet, right?PersistentMapK
simon.vergauwen
07/18/2019, 5:22 PMKind
.PersistentMap
addamsson
07/18/2019, 5:39 PMmapk.kt
PersistentMapK
a Foldable
apart from this?@extension
interface MapKFoldable<K> : Foldable<PersistentMapKPartialOf<K>>
simon.vergauwen
07/18/2019, 5:54 PMaddamsson
07/18/2019, 5:56 PMFunctor
?@extension
@undocumented
interface PersistentMapKFunctor<K> : Functor<PersistentMapKPartialOf<K>>
simon.vergauwen
07/18/2019, 5:57 PMPersistentMapKFunctor
becomes an interface with no abstract functions.addamsson
07/18/2019, 5:58 PMsimon.vergauwen
07/18/2019, 5:58 PMFunctor
that's only map
if I'm not mistaken.addamsson
07/18/2019, 5:58 PMmap
on my PersistentMapK<Int, Int>()
simon.vergauwen
07/18/2019, 5:58 PMaddamsson
07/18/2019, 5:58 PM@extension
@undocumented
interface PersistentMapKFunctor<K> : Functor<PersistentMapKPartialOf<K>>
mapk.kt
raulraja
07/18/2019, 6:00 PMsimon.vergauwen
07/18/2019, 6:00 PM@extension
@undocumented
interface PersistentMapKFunctor<K> : Functor<PersistentMapKPartialOf<K>> {
override fun <A, B> PersistentMapKOf<A>.map(f: (A) -> B): PersistentMapK<B> =
fix().map(f)
}
Now that you’ve implemented all abstract methods of interface Functor<F>
, we can no generate fun <K> PersistentMapK.Companion.functor(): Functor<PersistentMapKPartialOf<K>> = object : PersistentMapKFunctor<K> { }
raulraja
07/18/2019, 6:01 PMaddamsson
07/18/2019, 6:01 PMsimon.vergauwen
07/18/2019, 6:01 PMFunctor
provides with faster implementations, but that’s optional.map
on PersistentMapK
?addamsson
07/18/2019, 6:02 PMraulraja
07/18/2019, 6:05 PMsimon.vergauwen
07/18/2019, 6:06 PMfix
. I.e. fix().map(f)
.
So if we look at Option
it looks like this with only the Functor impl.
@higherkind sealed class Option<out A> : OptionOf<A> {
object None: Option<Nothing>()
data class Just<A>(val a: A): Option<A>()
fun <B> map(f: (A) -> B): Option<B> = ...
}
interface OptionFunctor : Functor<ForOption> {
fun <A, B> OptionOf<A>.map(f: (A) -> B): Option<B> =
fix() //returns Option<A>
.map(f)
}
addamsson
07/18/2019, 6:07 PMMapK
simon.vergauwen
07/18/2019, 6:08 PMaddamsson
07/18/2019, 6:08 PMPersistentMapKOf
simon.vergauwen
07/18/2019, 6:08 PMPersistentMapKOf
is simply typealias PersistentMapKOf<K, V> = Kind2<ForPersistentMapK, K, V>
PersistentMapK<K, V>
is equal to PersistentMapKOf<K, V>
and additionally we can tell the compiler that PersistentMapK
has all the functions that are implemented by the typeclasses for ForPersistentMapK
.addamsson
07/18/2019, 6:10 PMsimon.vergauwen
07/18/2019, 6:10 PMaddamsson
07/18/2019, 6:11 PMsimon.vergauwen
07/18/2019, 6:11 PMaddamsson
07/18/2019, 6:11 PMmap
like this:@extension
@undocumented
interface PersistentMapKFunctor<K> : Functor<PersistentMapKPartialOf<K>> {
override fun <A, B> Kind<PersistentMapKPartialOf<K>, A>.map(f: (A) -> B): Kind<PersistentMapKPartialOf<K>, B> {
TODO("not implemented")
}
}
simon.vergauwen
07/18/2019, 6:12 PMFunctor
addamsson
07/18/2019, 6:12 PMPersistentMapK
is already a kind and kapt can work its magic> Task :arrow-persistent-data-structures:kaptKotlin FAILED
e: error: Arrow's annotations can only be used on Kotlin classes. Not valid for error.NonExistentClass
after each build?PersistentMapKOf
from PersistentMapK
then rebuild then re-add itsimon.vergauwen
07/18/2019, 6:23 PM@higherkind
and @extension
in the same module?@higherkind
boilerplate in the module for now.addamsson
07/18/2019, 6:24 PMcompanion object
to PersistentMapK
remove
is not going to work since Foldable
folds the valuesremove
works with the key
, not the value 😞Foldable
the wrong waysimon.vergauwen
07/18/2019, 7:14 PMOptics
.Foldable
wrong. We don't know the type of the key
inside Foldable
.addamsson
07/18/2019, 7:20 PMkey
in order to implement an efficient remove
operationsimon.vergauwen
07/18/2019, 7:20 PMkey
for FunctorFilter
.addamsson
07/18/2019, 7:20 PMremove
function in PersistentMap
simon.vergauwen
07/18/2019, 7:21 PMIndexedFoldable
or IndexedFunctorFilter
addamsson
07/18/2019, 7:21 PMremove(element)
function anywaysimon.vergauwen
07/18/2019, 7:22 PMaddamsson
07/18/2019, 7:27 PMsimon.vergauwen
07/18/2019, 7:30 PMaddamsson
07/18/2019, 7:30 PMMissing newline before ")"
)
at the end of the line (just like in Clojure)simon.vergauwen
07/18/2019, 7:56 PMaddamsson
07/18/2019, 8:09 PMImran/Malic
07/19/2019, 11:52 AMfun <A> Kind<F, A>.remove(element: A,
monoidK: MonoidK<F>,
AF: Applicative<F>): Kind<F, A> {
simon.vergauwen
07/19/2019, 11:56 AM./gradlew ktlintFormat
should fix all style isssues, and the onces it cannot like *
imports will get linked in the console.addamsson
07/19/2019, 12:54 PMsimon.vergauwen
07/19/2019, 12:54 PMaddamsson
07/19/2019, 1:36 PMTraverse
typeclass, but we discussed Foldable
with @simon.vergauwensimon.vergauwen
07/19/2019, 1:48 PMTraverse
inherits from Foldable
and Functor
and is a very popular and powerful typeclass to do operations.
fun getUserById(id: Int): IO<User> = ...
val operations: List<IO<User>> = listOf(1, 2, 3).k().map(::getUserById)
val sequenced: IO<List<User>> = operations.sequence(IO.applicative())
val traversed: IO<List<User>> = listOf(1, 2, 3).k().traverse(IO.applicative()) { id -> getUserById(id) }
Having List<IO<User>>
is useless to work with so you need to be able to swap the order of the contexts.IO.parApplicative()
which we discussed earlier will automatically make this work in parallel. Which is exactly what we I did in that PR I shared, parTraverse
is just an alias for traverse
with parApplicative()
.addamsson
07/19/2019, 2:06 PMIO
?Traverse
simon.vergauwen
07/19/2019, 2:08 PMio.reactivex.Single
, reactor.Mono
which are both pure and fairly principled. You can also see it as a FP version of kotlinx.coroutines.Deferred
, or Scala Future.addamsson
07/19/2019, 2:08 PMsimon.vergauwen
07/19/2019, 2:08 PMIO
uses coroutines for threading under the hood yesaddamsson
07/19/2019, 2:08 PMIO
or Traverse
?Imran/Malic
07/19/2019, 2:08 PMaddamsson
07/19/2019, 2:09 PMIO
~ Deferred
or Traverse
~ Deferred
?simon.vergauwen
07/19/2019, 2:09 PMIO
~ Deferred
Imran/Malic
07/19/2019, 2:09 PMaddamsson
07/19/2019, 2:09 PMImran/Malic
07/19/2019, 2:10 PMaddamsson
07/19/2019, 2:10 PM0
Scala knowledgesimon.vergauwen
07/19/2019, 2:10 PMaddamsson
07/19/2019, 2:10 PMsimon.vergauwen
07/19/2019, 2:11 PMaddamsson
07/19/2019, 2:11 PMsimon.vergauwen
07/19/2019, 2:12 PMF
and you want to run functions that return G
. So in our example our original context was List
and we wanted to run functions that return IO
.
But a simpler example maybe.
fun getOptionalUser(id: Int): Option<User> = ..
listOf(1, 2, 3).k().traverse { id -> getOptionalUser(id) }
List<Option<User>>
but lets say I am not interested in having empty values (None
) in my list.
So I want to short-circuit just like `map`/`flatMap` for Option
and have an Option<List<User>>
which either contains all users or None
.addamsson
07/19/2019, 2:14 PMlistOf(1, 2, 3).k().traverse { id -> getOptionalUser(id) }
return?simon.vergauwen
07/19/2019, 2:14 PMOption<List<User>>
addamsson
07/19/2019, 2:15 PMFunctor
) but also folds Foldable
?simon.vergauwen
07/19/2019, 2:16 PMinterface Traverse<F> : Functor<F>, Foldable<F>
addamsson
07/19/2019, 2:16 PMApplicative
?Apply
simon.vergauwen
07/19/2019, 2:17 PMApplicative<G>
so that you can use map2
to combine 2 G
elements.addamsson
07/19/2019, 2:17 PMsimon.vergauwen
07/19/2019, 2:19 PMfun Kind<G, Kind<F, A>>.sequence(AF: Applicative<F>): Kind<F, Kind<G, A>> = traverse(AF, :identity)
val listOfOptions: List<Option<Int>> = listOf(1.some(), 2.some())
listOfOptions.sequence() //Some([1, 2])
listOf(1.some(), None).sequence() // None
listOf(1, 2).map { Some(it) }.sequence()
is the same as listOf(1, 2).traverse { Some(it) }
addamsson
07/19/2019, 2:23 PMsimon.vergauwen
07/19/2019, 10:43 PMraulraja
07/23/2019, 8:58 AMsimon.vergauwen
07/23/2019, 8:59 AMremove(a).find { a } == false
but currently we haven't defined any yetraulraja
07/23/2019, 9:00 AMImran/Malic
07/23/2019, 9:18 AM