other-languages

john 2024-04-09T21:18:43.762129Z

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

2024-04-09T23:43:19.557409Z

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)

2024-04-09T23:45:40.988879Z

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.

john 2024-04-09T23:47:59.762679Z

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?

2024-04-09T23:50:59.859399Z

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

2024-04-09T23:51:23.435609Z

say you want to add some stateful stuff using the State monad

2024-04-09T23:52:03.350539Z

if you just naively State Option Int, then you have to double up every time you use return and bind gets really tricky and

2024-04-09T23:52:42.489659Z

so what you want to do is have a single monad that has the behavior of both State and Option

2024-04-09T23:55:26.568539Z

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

john 2024-04-09T23:58:32.795969Z

Hmm, seems handy

2024-04-09T23:58:58.731869Z

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

john 2024-04-10T00:05:07.836729Z

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?

john 2024-04-10T00:09:37.083419Z

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?

john 2024-04-10T00:10:01.956759Z

I mean the author of StateT or SomeMonadT

john 2024-04-10T00:16:55.922919Z

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

john 2024-04-10T00:19:22.023629Z

Interestingly here, we're letting you use any Clojure data manipulation tools you want as your merge strategy between functions and deriving behavior

john 2024-04-10T00:21:54.062749Z

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

john 2024-04-10T00:23:54.823909Z

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

john 2024-04-10T00:33:55.866159Z

So I'm curious what merge method monad transformers are using and what that looks like for them

john 2024-04-10T00:34:51.085539Z

And can they update these transformer stacks at runtime? Or is it purely a compile time thing, like a type system thing?

john 2024-04-17T12:08:11.757389Z

Here's an updated prototype of the idea: https://github.com/johnmn3/step-up/blob/main/src/step_up/alpha/user.cljs

john 2024-04-09T22:07:11.503659Z

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!

john 2024-04-09T22:16:10.723999Z

And, to what extent can they update monad transformer stacks at runtime?

john 2024-04-09T22:17:47.075319Z

And do they give you hooks for running code when a transformer is constructed, rather than when called?