6 ms·
> very hard to understand why a function could be useful in the first place So true. https://hackage.haskell.org/package/base-4.9.1.0/docs/Control-Monad-Fix.h
by m-j-fox 9y ago
> very hard to understand why a function could be useful in the first place
So true.
https://hackage.haskell.org/package/base-4.9.1.0/docs/Control-Monad-Fix.html https://hackage.haskell.org/package/base-4.9.1.0/docs/Contro...
> mfix :: (a -> m a) -> m a
> The fixed point of a monadic computation. mfix f executes the action f only once, with the eventual output fed back as the input. Hence f should not be strict, for then mfix f would diverge.
But why tho?
- z1mm32m4n 9y agoBy analogy to fix: fix f --> f (fix f) That is, to compute the fixed point of some function f, we give f access to the fixed point (i.e. fix f) in order to compute the fixed point. Consider f is strict, and that we try to run this: fix f --> f (fix f) --> f (f (fix f)) --> f (f (f (...))) Why? Because if f is strict it has to evaluate its argument before evaluating the body. On the other hand, if f is non-strict, we can evaluate its body without evaluating its argument first. mfix generalizes fix to monadic functions. When we have monadic functions, we're usually dealing with side-effectful computation, so we want to make sure that we only force the monadic action f once, so that the side effects only run once. --- Another question to ask is "what is fix even useful for"? fix is how we introduce general recursion into a language. For example, the lambda calculus has fix, except it's called the Y combinator. Compare: Y f --> f (Y f) fix f --> f (fix f) The lambda calculus is Turing complete because it has this general recursion. fix is often used in smaller toy languages to also make them Turing complete, because this form of recursion is straightforward. While Haskell has recursive function definitions, fix (and mfix more generally) are sometimes useful in their own right.
- dllthomas 9y agoI think you answered the wrong "why".
- ashark 9y agoUbiquitous single-letter symbols mapping to who-knows-what possible things, pnflly abvted fncn nms, and unclear motivations for code are what I've bounced off of with Haskell every time I've tried to dig in. The community seems to have adopted all the worst parts of mathematics culture, along with whichever good parts they've brought in.
- draw_down 9y agoYeah, reading that shit drives me nuts.
- wereHamster 9y agoSerious question. In the following code, what would be better function / type names? (or you can pick another bit of Haskell code that you know and/or have particular trouble understanding). class Foldable t where foldl :: (b -> a -> b) -> b -> t a -> b
- nv-vn 9y agoI've always disliked mapM_. Something about it just looks ugly and it doesn't really hint what its for. I would like to see mapM as mapMonad and mapM_ as eachMonad. Of course, once I knew what it meant the abbreviations became rather nice.
- deleted 9y ago[deleted]
- nimih 9y agoMaybe something like: class Foldable myFoldable where foldl :: (summary -> element -> summary) -> summary -> myFoldable element -> summary This is the translation I do in my head when I read that type signature, at least.
- uryga 9y agoThe standard version [ fix :: (a -> b) -> b ] is weird too - I'd suggest reading up on that first, there's a lot of tutorials. You can use fix to write recursive functions, so I'm guessing mfix is analogous, it just handles some >>= plumbing for you.
- aninhumer 9y agoSee: https://wiki.haskell.org/MonadFix https://wiki.haskell.org/MonadFix Basically it's the primitive that allows monadic computations to be written in the same lazy cyclic style as regular values in Haskell. (e.g. `ones = 1:ones` to create an infinite lazy list of 1.) There isn't a single answer to "why?" any more than there is for monads in general, but as an example, I've been looking into using this abstraction to model circuit graphs.
- lmm 9y agoIt's useful if you want to e.g. traverse a custom tree structure with an effectful function. You can write the function that just does one level of the tree and pass it to mfix to get a version that does the whole tree recursively, composing the effects in the way that naturally makes sense for that kind of effect.
- pka 9y agoHere's a practical application of mfix: mfix $ \threadId -> forkIO $ do -- computation in forked thread forkIO creates a new thread and returns its thread id. However, in order to access the returned thread id inside the forked computation, normally one would have to store it in a variable and then read from it inside the thread. mfix, by nature of its laziness, captures the return value of the function passed to it, and passes it to said function.
- conistonwater 9y agoIt's a very neat example, and I like it very much as an illustration of mfix, but wouldn't one just use Control.Concurrent.myThreadId for that? Seems a little overkill.
- pka 9y agoHa, I guess this must be some kind of selective blindness then :) I've seen myThreadId many times, it makes perfect sense, and yet, whenever I've had to grab the spawned thread id I've always reached for mfix... Well, in this particular case there's of course no need for that. But the general pattern applies to many things. One other example that immediately springs to mind is e.g. registering a callback while needing the ability to unregister the callback from within itself, using some sort of id returned from the registering function.
- Tarean 9y agoReflex is a frontend library for haskell. -- create an input text widget with auto clean on return and return an event firing on return -- containing the string before clean inputW :: MonadWidget t m => m (Event t T.Text) inputW = do rec -- fire send event when pressing enter let send = textInputGetEnter input -- textInput with content reset on send input <- textInput $ def & setValue .~ ("" <$ send) -- tag the send signal with the inputText value BEFORE resetting return $ tag (current $ input ^. textInput_value) send Ignoring the operator soup for a second (thanks lens) you might notice that the send event depends on the input field and the content of the input field depends on the send event. In, say, java you would first create the field and then mutate it separately. In haskell mutating wouldn't work so instead we usually tie the knot which means making the value dependent on itself and letting laziness figure it out. However we are creating a text field here so if we make the value recursive on itself we are gonna be spawning text fields all over the place. MonadFix is variant of this that splices the effect out so it is only run once and then allows the value to be recursive.
- evincarofautumn 9y agomfix is somewhat low-level. It’s used in the desugaring of Haskell’s “do rec” notation. I find it useful because it lets me take a recursive structure[1] and a multi-pass algorithm[2] on that structure, which involves side effects[3], and express it in code as a single pass, without mutation. This conversion of multi-pass algorithms to a single pass while retaining time/space complexity guarantees is one of the key benefits of laziness in general. [1]: e.g., an AST [2]: e.g., type inference, where pass 1 is generating type constraints, and pass 2 is solving the constraints and annotating the tree with the final inferred types [3]: e.g., generating fresh type variables
- swift 9y agoI'd love to hear more detail about the example you give of type inference over an AST. I like the idea, but I'm having trouble envisioning how to write code in that style cleanly. Did you implement that in an open source project I could take a look at?
- evincarofautumn 9y agoIt’s not the prettiest code, but here: https://github.com/evincarofautumn/kitten/blob/8a7e949f1af71384a5fb56693de32474dead492c/new/lib/Kitten/Infer.hs#L123 https://github.com/evincarofautumn/kitten/blob/8a7e949f1af71... The key line is: (term', t, tenvFinal) <- inferType dictionary tenvFinal' tenv term In “inferType”, I just annotate each term with “Zonk.type_ tenvFinal type_” as I go, in the same pass as generating the type constraints. Since “tenvFinal” is the final type environment, and “zonking” substitutes all type variables with their solved types, everything gets lazily resolved when reading the annotated types later on, e.g., during analysis & codegen.
- swift 9y agoThanks, that helped me understand better. That's really cool!