Implement MonoidInstance in terms of Semigroup. Haskell’s interface to making a Monoid is to define types and override defaults of either mempty or mconcat. mappend is defined in terms of Semigroup. I think `template M of SemigroupInstance` would be what we are looking for here so that mappend can have a default implementation of returning the associative function from the Semigroup.
It might be easier for users to implement just Monoid sometimes?
Do we need to ?
Haskell is good with constructing hexagons. What aspects of haskell do that? Are they included? Just providing a base to work with the given structures (ie extending Maybe or reusing it) The adapters Implementations of the interfaces + test helpers for the laws
Write the above in the docs.
Also rethink and add any aliases/names. Partial might need an object instead of returning an anonymous function.
By returning a function and with the help of partial application.
Cheers to Olle from phpc
fmap :: (a -> b) -> (f a -> f b)
So that we return a function and once that function is called with arg: f a then we call fmap on it? I suppose so. Does it work now?
function partialFmap(callable $f) {
return partial (fmap(...), $f)
}Should be it. Leaving this here still as it might be useful?
https://stackoverflow.com/questions/53854853/why-is-there-a-distinction-between-co-and-contravariant-functors-in-haskell-but Also called Categorical Functor but seems like this generalisation can be very useful. https://hackage.haskell.org/package/categories-1.0.7/docs/Control-Categorical-Functor.html
I think after looking around at typeclasses and different definitions of composition, I need a diagram. Maybe it is actually a category!
Something like this https://wiki.haskell.org/index.php?title=Foldable_and_Traversable#What_do_these_classes_all_mean?_A_brief_tour:
And inline them all in a single Closure rather than one on top of the other. Would that be possible?
Notations can do multiple composition but not with inline as per above.
So there are things that are functional and are written elsewhere. These things have a different mapping from Concepts to Implementations of those concepts. For example a type class might be mapped with an interface and each instance as a class implementing that interface. Alternatively might be mapped from an abstract class or a simple function. As programmers, we do that daily, we map from one abstract concept of our understanding into the structures defined in the language, with varying success and precision / abstraction as we see fit.
Kleisli categories mapping might be interesting? Or we are looking to something more abstract? Ie. Morphisms in Kleisli are the embelished version, separating an implementation from the abstraction. Bridge pattern rings a bell here.
Trying to better explain, in a Kleisli category one will define two things when defining the composition function of the category. How the non-embelished functions compose and how you `mappend`. So the question here is, is this all we need to generalise the mapping from Category to some implementation of it (ie Haskell / PHP)? The non-embelished functions will provide the mapping to the implementation and the `mappend` operation will provide the way to add/append/combine those together (sequentially?). Essentially giving better semantics, or better API to work with these things (as “implementation” details have been now abstracted).
Could touch on this, at least?
Like do notation for example. In PHP (or anywhere) we could abstract a notation to a function if we project the elements/parts as arguments of the function and then define how they compose. For example imagine the below types:
runNotation :: [a] -> b
Takes a list of “a”s and runs them and produces a b (which also could be again an “a” but just to signify the difference).
So the `dn` function we have now is like:
dn in php: (MonadInstance $ma, MonadInstance|\Closure/*|Composition*/ …$fs) enforcing the first argument to be a MonadInstance, fine, lets add this restriction for a second
runNotation :: a’ -> [a] -> b
Where a’ is a “restricted” part of what a is.
dn :: a’ -> [a] -> b dn :: MonadInstance -> [MonadInstance|Closure] -> MonadInstance
a “then” (or notation for (>>)) would look like this:
notationForThen :: a’ -> [a] -> b notationForThen :: MonadInstance -> [MonadInstance] -> MonadInstance
So we could really generalise here and write even: notationForThen :: [MonadInstance] -> MonadInstance
However in general for notations, we could project the elements into the argument list and decide how these compose:
someNotation(
$element1,
$element2,
$element3
);So we need 2 things:
- what the type of the elements is
- how to compose those
- (optionally) how you fold the list of elements (for more flexibility in expression) - ie foldr or foldl
So with CategoryInstance we could define notations as long as we define those two above. Restrictions can go in the helper function.
So do-notation could be
doNotationDefinition :: [elementsType] -> result doNotationDefinition :: [m a | (a -> m b)] -> m b
A notation for function composition (right-to-left, if I am correct), ie:
// g after f
// g . f
// = \x -> g ( f x )
//
functionComposition(
$g = fn ($x) => $x + 3,
$f = fn ($x) => $x * 3,
);Would be in types:
functionCompositionNotation :: [(a -> b)|(b -> c)] -> (a -> c)
Could use orders to make sure they come correctly instead of “any” from the sum type above, although this would still work but a list of [(a->b)] would just -> (a -> a) basically making all type variables the same (because in this case they actually are), but just in the case of function composition maybe?
Nevertheless, it is still true:
functionCompositionNotation :: [(a -> b)|(b -> c)] -> (a -> c) functionCompositionNotation xs = foldTheListSomehow (myCompositionFunction) xs myCompositionFunction = (.) foldTheListSomehow = <define the fold from right to left, i guess, or generally how we iterate and consume>
So could make it a type-class thing.
This makes a lot of more sense now: https://www.haskellforall.com/2012/08/the-category-design-pattern.html in the “lens” of making notations. It is just that haskell allows infix functions, in PHP we do not have the ability to define infix functions ourselves, however we can “project” this infix notation to an list of arguments, as if we were composing them with the infix operator. Might actually seem trivial, now.
Additionally you could say also apply “Foldable” after “Category”, but probably is better to “inline” it for performance.
Ie: Some notation would be implemented by folding a list (right to left) and composing according to the composition function assigned to the Category of those elements.
Now that we have notations, so that we can re-implement dn on top of it.
Consts or defines. See https://github.com/ihor/NSPL/blob/master/nspl/f.php#L135
In Haskell, you might memoize with recursion. See https://wiki.haskell.org/index.php?title=Memoization It’s worth looking into this.
Many instances do not have helper assertions for their laws.
It seems FunctorAdapter now have less use, maybe can move it to a
function that returns an anonymous class? These “adapters” are not so
convenient as the typeclass instance registration though but they are
probably faster unless you keep combining them (magic method __call
will keep delegating further in this hierarchy of inheritance).
It is a use case decision. For one-off it might be still worth it. Nevertheless anyone can implement their own FunctorInstance class and be done.
I am just not 100% against it yet.
Example in Haskell
data State = Unbound | Bound | Listening | …
data Socket (s :: State)
bind :: Socket Unbound -> IO ()
listen :: Socket Bound -> IO ()I think phpstan documentation implements a “kind”.
Essentially implementing “typestates”.
PHP’s array_unique does a (string) $a == (string) $b comparison, so it
cannot be used if the elements cannot be casted to string.
https://www.tweag.io/blog/2020-01-16-data-vs-control/
And also here we could elaborate on the Data.IO and Control.Monad.IO so that the latter implements in terms of monadic operations. What about the first though?
This has taken place, more or less.
-- | @since base-2.01
instance Applicative ((->) r) where
pure = const
(<*>) f g x = f x (g x)
liftA2 q f g x = q (f x) (g x)
-- | @since base-2.01
instance Monad ((->) r) where
f >>= k = \ r -> k (f r) r
Implemented with Arr.
Example for : x -: f = f x
SyntaxHelper::start(3)
->apply(f(...));
// or "stack" based
StackSyntaxHelper::push(3)
->push(f(...))
->apply();
StackSyntaxHelper::push(3, 4, 6)->apply('max');
StackSyntaxHelper::push(3, 4, 6, 'max')->applyFirstCallable();First two do not look much of a helper really.
Implemented in terms of notations.
Lets take for example Maybe. Maybe has 1 type variable `a`. If Maybe is implemented in a single class there might be a type `a` that cannot be an instance of Show but is Functor for example. Implementing it in a single class may be quite restrictive as the type variable `a` will have to implement all requirements coming from the PHP interfaces (that try to map a typeclass) ? Is this right?
Implemented the MethodContainer.
I think “Composition” became natural because of partial application. Implemented rl / lr
What needs to be fixed here?? Will never remember.
Calling this Done and if there is an issue will solve it there.
Calling this Done and if there is an issue will solve it there.
TimWolla and Edorian helped my understand that callable is scope specific and how to benchmark a little better.
It makes sense to change all type declarations from callable to
\Closure for performance reasons, even if it forces the user to use
\Closure::fromCallable it probably is faster as that creates a more
performant \Closure.
There’s more for performance but this is a little start.
Now it is fmap(Composition|callable $f, F $g): F But as the argument $g has to
be a functor, we can accept a callable if we wrap it in composition (that will
also apply partial).
See https://stackoverflow.com/questions/38034077/what-is-a-contravariant-functor specially https://stackoverflow.com/a/56150133
instance Contravariant (Op a) where
contramap :: (b' -> b) -> (Op a b -> Op a b')
contramap f g = Op (getOp g . f)
contramap :: (b -> a) -> f a -> f b
The (b -> a) in Contravariant defines the “medium”, the way we are going to produce something that can consume b out of something that was consuming a.
I think this actually is easy to solve if we return the ReflectionFunction instance from Composition. After handling the infinite loop.
This seems such a central piece.
Probably have left some stuff from the containers.
Or contramap once implemented, something along the lines of:
cFlippedWithFmap ('abs') |> $maybeInt // Could then mean
fmap ('abs', $maybeInt);
// coming from calling `fmap()` in the $maybeInt object, cFlippedWithFmap would have to implement
// that in its __invokeWhen implementing fmap for an instance, we can always fmap() over a
Composition to have a central place where we can control things like
partial application or other `utilities`. It is however slightly more
expensive as it wraps things again and again and does a few extra
calls.
Ie for creating something with “referential transparency” See https://wiki.haskell.org/index.php?title=Referential_transparency
Or list comprehension? Some snippets that could be inspirational.
$some = 123456;
$var1 = $var2 = 'some';
foreach (['var1', 'var2'] as $k => $outer)
foreach (['new value', 'next value'] as $$outer)
funcWithTwoArgs($var1, $var2);
print "\n" . $var1;
print "\n" . $var2;function funcWithTwoArgs($var1, $var2) {
print_r ( compact('var1', 'var2') );
}
function listComp(iterable $variables, iterable $values) {
foreach ($variables as $k => $outer)
foreach ($values as $$outer)
funcWithTwoArgs($var1, $var2);
}
listComp(['var1', 'var2'], ['one', 'two']);Could avoid func(…) notation if every function (ie in Composition::__invoke) we check
how many arguments we have been given and either return a function or call the function.
Isn’t that essentially what we already do with partial ? Yes.. So this code:
function f($a, $b) { return $a + $b };
$f = c('f');
$partiallyApplied = $f(); // does not need to be c('f')(...) because `partial` returns a function now
$partiallyAppliedA = $f(123) // again partial returns a function but with one argI think there is inlining you can do on function composition but there is also inlining you can do on types, since types can be represented by functions, right?
https://en.wikipedia.org/wiki/Typestate_analysis https://www.tweag.io/blog/tags/linear-types/
It is a little vague how “safety” is defined. There is type safety or safe from side-effects.
Concentrating on the second, side-effects safety, one can define a function that wraps a given function call in a try-catch block. Generalising a little bit, one can define a “control” structure for the same.
Generalising even further, a type class for things that safely do something. This is a bit reverse from the idea of Maybe and Either on their face value.
Generalising to a different direction, if that even makes sense, one could say that anything that can be called can be called safely or unsafely. Therefore a control structure could capture all cases, allowing to opt-in for safety? More or less the implementation of try-catch is doing that.
One could also go sideways, defining towards the direction of Catch, providing a way to not only safely run something but also handle one or multiple catch cases.
There must be some intuition from Haskell and/or monads that do the same, or partial functions. See Control.Exception.
However, the level of generalisation is not something you can choose for all cases. Different generalisation is chosen depending on different solutions or implementations. One cannot say X implementation is all you need and captures all your possible scenarios and use-cases. This seems like an obvious observation now but maybe put some things into perspective.
The meaning of this last paragraphs concludes this journey, meaning that all different ways may or may not be interesting for some use-case or scenario therefore different approaches are still interesting to be implemented in this lib and it is up to the user to decide as they are the one controlling the level of generalisation in their “safety” choices that is desired each time.
On partial application currently there is a single function that sorts out the situation, however this seems to be hard to deal with the types. Would a more elaborate implementation, one that analyses the terms help with the types or even provide more features?
Previous notes from the todo item that turned into a journey:
Write tests for the actual usage, see that it works passing one argument at a time. Somewhere had to pass both at one call. Would an object help rather than a closure for the types?
I think fixing partial in respect to the types will help a lot with the static analysis errors.
Instead of Either a b we use the union types: a|b (sum types) And instead of Tuple<a,b> we use a&b (intersection types). This way we have some native support from PHP for product and coproduct (product and sum types). One thing I miss is that I cannot really make “new types” and define functions on them? Like I have to go write a function for every combination I want to use, as we do not have generics. But we did not have generics anyway. However let’s consider Maybe.
Maybe is Nothing | Just x, therefore a sum type, therefore coproduct. So I could write null|int (for Maybe Int) and null|object (for Maybe object) okay, and this also transforms to the more convenient nullable type ?int or ?object. Fine.
So let’s say I want to make a functor on that “Maybe” type. We need to define fmap.
fmap :: (a -> b) -> f a -> f b
Taking arbitrary types for a (int) and b (bool) we could have:
function fmap (\Closure(int):bool $f, ?int $a): ?bool {
return $a === null ? null : $f ($a);
}Let’s generalise wherever we still can and use type annotations
/**
* @template A1
* @template A2
* @template B1
* @template B2
* @param \Closure(A1|A2):B1|B2 $f
* @param A1|A2 $a
* @return B1|B2
*/
function fmap (\Closure $f, mixed $a): mixed {
return $a === null ? null : $f ($a);
}Now that is nice and good for something that only has two constructors, what if it had 3 ? Also notice the implementation cannot handle anything but “null” as one of the two types, as we do not know the types!
adrian.2688 — i’d stick those utility methods into the associated classes, as static methods ditch functions.php
also when your traits refer to a method it expects the using class to have, you should define that method on the trait as abstract more broadly, as you work with this i’d suggest thinking more about how the ideas could apply / be more “naturally” implemented in php, rather than just trying to port them directly. i don’t do haskell, but some of these concepts seem like they don’t accomplish much as php tools
Crell — Functions are fine, but they does seem a bit over engineered. And Left/Right eithers s**k. 🙂 Explicit Result eithers are better DX. I have my own composition centric library I’ve been using, and am now trying again to get into core.