Any Haskellers in here? I'm working on a function composition method we're tentatively calling a function transformer that is reminiscent of Haskell's https://hackage.haskell.org/package/transformers-0.6.1.1/docs/Control-Monad-Trans-Class.html. It might not work exactly the same as Monad Transformers. I don't think we're doing any "lifting" here. Below is a super rudimentary API for modeling parts of functions as pieces of data and then transforming the data of those functions so as to compose new ones, at compile time or run time. It includes transform stacks for various parts of a function, :ins, :outs, :args, :op, etc, etc. I'm wondering if anyone familiar with Haskell could give me the sherpa's tour through what they're talking about wrt Control.Monad.Trans and whether we should use any of the solutions their using for managing transform stacks, or any other features we should bring over. I stumbled on this transformer solution while trying to solve a front end UI problem with lots of hierarchical component functions and I only just recently found out about these parallels to monadic transformers. So if you have a minute, please review how this function transformer below works and let me know what we're missing from Haskell's monadic transformers or anything else we're missing, thanks! https://clojurians.slack.com/archives/C0904S2QJ/p1711520124934229?thread_ts=1711045846.649609&cid=C0904S2QJ
monad transformers a functions from a implementation of a monad to another implementation, you use them to compose say the option monad and the state monad together, a way to stack the effects of multiple monads, some alternatives to that are things like effects systems or free monad interpreters (which are used to build effect systems)
they don't seem to be connected in anyway to whatever this is. what you have kind of looks like netty pipelines or interceptors, but just too much too read without a succinct concrete explanation.
When you say "from a implementation of a monad to another implementation," are they manipulating the monad as a list data structure? Are they manipulating some monad environment data structure?
so in haskell you might be doing stuff using something like Option, which is a monad, and you computation might end returning an Int, so because you are operating in the Option monad the type of the computation becomes Option Int
say you want to add some stateful stuff using the State monad
if you just naively State Option Int, then you have to double up every time you use return and bind gets really tricky and
so what you want to do is have a single monad that has the behavior of both State and Option
of course you can't actually just smash them together or even re-use State, so you have StateT which is a monad transformer, given some type like Option which implements the Monad type class, you can apply StateT to it to get a new type that that also implements the Monad type class in a way that is a composition of State and Option
Hmm, seems handy
https://www.haskellforall.com/2012/08/the-category-design-pattern.html doesn't have anything to with monad transformers but might be a useful read
So, if someone were going to go and define SomeMonadT, would they be manipulating some Monad type class object, when turning SomeOtherMonad into a SomeMonad? Are these monads functions or just type declarations?
Or, from the perspective of the user, is it just some MagicMerge StateMonad OptionMonad and the user doesn't really know how these things are merged?
I mean the author of StateT or SomeMonadT
My understanding is that the transform is largely based on selectMany, in some scala implementation. Perhaps it'd be some merge strategy using select-keys
Interestingly here, we're letting you use any Clojure data manipulation tools you want as your merge strategy between functions and deriving behavior
But a user can destructively change behavior of it's own super, so it might not be an exact "natural transformation" in the category theory sense. I'd imagine a derived function that can reproduce all the same inputs and outputs of some parent/super implementation constitutes a natural transformation, embedding the old transformation entirely in the new transformation, which is easy for us to do by convention in Clojure
Nevertheless, it seems to be similarly transforming "one functor into another" in the category theory sense of the term "transformation," to the extent that functions can be considered mappings between categories of inputs and outputs
So I'm curious what merge method monad transformers are using and what that looks like for them
And can they update these transformer stacks at runtime? Or is it purely a compile time thing, like a type system thing?
Here's an updated prototype of the idea: https://github.com/johnmn3/step-up/blob/main/src/step_up/alpha/user.cljs
As an example, in this todomvc code we're using transformers to manage state in component functions with state management transforms (`a/selected?`) mixed in 'such that computations of the old [transformer] may be embedded in the new one':
(def filter-anchor
(-> comp/a
(update :id conj ::filter-anchor)
(update :with conj a/selected? a/void-todo styled/filter-anchor)
(assoc-in [:props :on-selected]
#(-> % (assoc-in [:style :border-color]
"rgba(175, 47, 47, 0.2)")))))
(def filter-all
(-> filter-anchor
(update :id conj :all)
(update :children conj "All")))
(def filter-active
(-> filter-anchor
(update :id conj :active)
(update :children conj "Active")))
(def filter-done
(-> filter-anchor
(update :id conj :done)
(update :children conj "Completed")))
Once you've transformed things into appropriate leaf nodes, you can hang them in a hiccup tree like normal:
(defn footer-selectors []
(let [showing @(subscribe [:showing])]
[comp/container
[comp/item {:xs 3}
[c/filter-all {:selected? showing}]]
[comp/item {:xs 5}
[c/filter-active {:selected? showing}]]
[comp/item {:xs 4}
[c/filter-done {:selected? showing}]]]))
But if you wanted to you could continue to manage footer-selectors as a transformer and simply splice those anchors manually into the :children vector in the data. I just like representing the final leaves as they're organized in the html to be laid out in hiccup. It's easy to read. The transformers are moreso for the library side of your components, where they're built up out of one another.
Composing function components in hiccup is great, except when your closing over implementation details that make it less general for later wrappers. Composing them with transformers doesn't close over some of their implementation details, so you can transform implementation details that would have otherwise been closed over. So reconfiguring function implementations and growing code rather than changing it or defensively copying it are my main motivations for exploring this transformer method.
So, the question is, to what extent do you think this method relates to monadic transformers? And what are we missing here that we should bring over? Thanks again for your input!And, to what extent can they update monad transformer stacks at runtime?
And do they give you hooks for running code when a transformer is constructed, rather than when called?