The second-to-last post[0] talks about how they decided to migrate their stack from Ruby on Rails to Haskell, and are now in the seventh (!) year of that migration.
I'm very happy with my Bellroy Card Sleeve wallet. You know, just in case anyone's checking the comments on a functional programming article for wallet recs. As one does.
I thought this was a joke about two things having the same name… nope!
One funny thing about software is that beautiful things can emerge from the most unexpected places. I appreciate that there are folks out there with the bravery to share their journey.
> We want to build a structure that is essentially a syntax tree of the operations we want to perform.
A-a-and so we went from programming in Haskell to creating a new DSL, with an interpreter for it in Haskell, and programming in that DSL. Which kinda begs a question: you already have a perfectly serviceable programming language (i.e. Haskell) at hand; why not just use it?
This is a really good question. If you write all the queries directly in Haskell, you get an IO action which you can't inspect: all you can do is execute it and see what it does. It might make requests, it might repeat requests it didn't need to make, it could do literally anything else.
Other programming traditions also have this pattern of inventing minilanguages and interpreters for them; regular expressions are by far the most successful example. You could hand-roll string matching by writing your matching functions by hand, but it's often much easier to ask a regex library to run an interpreter over a string describing the pattern to match.
In Haskell, it's really cheap to invent data structures, so using the same language to describe the work is quite convenient. Laziness also means you almost never materialise the entire intermediate "work to be done" structure - you build little bits as the interpreter demands them. So it doesn't feel as heavyweight as an eDSL in some other language.
That's what I also don't quite get. I would build it as the parsing step mapped directly over the query. I suppose their approach lets them build a tree of operations but I'm skeptical it provides all that much benefit.
I don't understand your complain. Haskell is entirely aimed at creating those small languages and interpreting them. While at the same time the compiler they have in hand implements a DSL for describing imperative programs that do not allow the kind of analysis the author is doing.
A recurring problem, somehow making it very easy to write code that deals with 1 thing from start. When time comes , somehow hard to write code that deals with N things.
I wonder how different the code would look if it was just written to deal with N things from the start.
I’m also not sure how far this code can go, if I have queries that depend on responses of preceding queries , how will my runAp_ give me this? It probably won’t.
always wondered where are http frameworks that just give me a batch of requests to deal with from the start.
> I have queries that depend on responses of preceding queries , how will my runAp_ give me this? It probably won’t.
It definitely won't, which is what I was trying to get at with the discussion of monads and data dependencies. Applicatives by definition cannot have one "effectful" computation depend on the result of another. You could do a large bunch of parallel work until you need to pass a result into a function that decides what additional work to perform, at which point you need a monad. More advanced frameworks like Haxl apparently make this distinction explicit, so your computation proceeds as a sequence of batched parallel options, combining as much work as possible.
I am really really surprised Bellroy could afford to hire developers for their own store. I guess they are now much bigger than I thought? I watch them grow from an unknown brand that focus on slimming your wallet in the early 10s, to now I bump into people who have actually heard of used it. Or at least seen the online ads. Pretty amazing.
I remember Gwyneth Paltrow said something along the line moving away from Shopify was the biggest mistake she made with her online shop. I think that was before Pandemic and Shopify have improved a lot since then.
Which makes me wonder if it make sense for Bellroy to continue their path.
I love me some functional programming, and spend most of days happily immersed in F#, but when I see Haskell programmers casually discussing the lengths they’ll go to to avoid side-effects, I am always in awe. It’s like overhearing monks in the Middle Ages talking about their favorite techniques for copying the Bible by hand.
This seems like a cool article, but it isn't readable without Haskell background knowledge.
> For an intuition why this is true, consider that the constant functor Const r has an Applicative instance whenever r is a monoid, because pure stores a mempty value and (<*>) combines the held values with (<>). For a fun exercise, implement runAp_ in terms of runAp and Const.
rich_sasha|4 months ago
titanomachy|4 months ago
[0] https://exploring-better-ways.bellroy.com/designing-for-the-...
spiffytech|4 months ago
leohonexus|4 months ago
[0] https://flox.dev/nixinthewild/nix-in-the-wild-bellroy/
nikita2206|4 months ago
I have had one of their cardholders for 10 years now, it is incredible how durable and practical that thing is.
riwsky|4 months ago
chaboud|4 months ago
One funny thing about software is that beautiful things can emerge from the most unexpected places. I appreciate that there are folks out there with the bravery to share their journey.
throwthrow0987|4 months ago
tinyspacewizard|4 months ago
Joker_vD|4 months ago
A-a-and so we went from programming in Haskell to creating a new DSL, with an interpreter for it in Haskell, and programming in that DSL. Which kinda begs a question: you already have a perfectly serviceable programming language (i.e. Haskell) at hand; why not just use it?
_jackdk_|4 months ago
Other programming traditions also have this pattern of inventing minilanguages and interpreters for them; regular expressions are by far the most successful example. You could hand-roll string matching by writing your matching functions by hand, but it's often much easier to ask a regex library to run an interpreter over a string describing the pattern to match.
In Haskell, it's really cheap to invent data structures, so using the same language to describe the work is quite convenient. Laziness also means you almost never materialise the entire intermediate "work to be done" structure - you build little bits as the interpreter demands them. So it doesn't feel as heavyweight as an eDSL in some other language.
kqr|4 months ago
lkey|4 months ago
marcosdumay|4 months ago
What exactly you expected them do write?
vjerancrnjak|4 months ago
I wonder how different the code would look if it was just written to deal with N things from the start.
I’m also not sure how far this code can go, if I have queries that depend on responses of preceding queries , how will my runAp_ give me this? It probably won’t.
always wondered where are http frameworks that just give me a batch of requests to deal with from the start.
_jackdk_|4 months ago
It definitely won't, which is what I was trying to get at with the discussion of monads and data dependencies. Applicatives by definition cannot have one "effectful" computation depend on the result of another. You could do a large bunch of parallel work until you need to pass a result into a function that decides what additional work to perform, at which point you need a monad. More advanced frameworks like Haxl apparently make this distinction explicit, so your computation proceeds as a sequence of batched parallel options, combining as much work as possible.
ksec|4 months ago
I remember Gwyneth Paltrow said something along the line moving away from Shopify was the biggest mistake she made with her online shop. I think that was before Pandemic and Shopify have improved a lot since then.
Which makes me wonder if it make sense for Bellroy to continue their path.
fwlr|4 months ago
munchler|4 months ago
tome|4 months ago
Can you give an example of that happening?
ronef|4 months ago
farhanhubble|4 months ago
tinyspacewizard|4 months ago
> For an intuition why this is true, consider that the constant functor Const r has an Applicative instance whenever r is a monoid, because pure stores a mempty value and (<*>) combines the held values with (<>). For a fun exercise, implement runAp_ in terms of runAp and Const.
Really?