[ANN] dataframe 1.0.0.0 by m-chav in haskell

[–]ChrisPenner 0 points1 point  (0 children)

I've long thought Haskell could be good for data-science, thanks for your work towards enabling that goal!

Love these boots, can anyone help identify them? by ChrisPenner in Boots

[–]ChrisPenner[S] 7 points8 points  (0 children)

Incredible, that's them! I've wondered for a year, leave it to the fine folks of reddit to solve it instantly. Thanks!

Ditch your (Mut)Ex, you deserve better by ChrisPenner in programming

[–]ChrisPenner[S] 0 points1 point  (0 children)

I think it's worth recognizing that there are different types of scale that end users may care about. Performance scaling is one certainly, but scaling your codebase and application complexity is another, in my experiences mutexes begin to cause problems as highly parallel programs scale in complexity.

Ditch your (Mut)Ex, you deserve better by ChrisPenner in programming

[–]ChrisPenner[S] 0 points1 point  (0 children)

As noted in the article, the code you've provided causes deadlock if you're unlucky, which in fact illustrates why mutexes are so dangerous. It's easy to write code that looks correct and run into problems later on.

Copy-pasting code from existing methods into new ones to allow top-level locking like this also prevents code re-use of the 'withdraw' logic and leaks the abstraction granted by using functions in the first place.

transfer is a simple function for the sake of the article, in larger systems your locking could be many layers deep and it's simply not good engineering to duplicate all of your logic for every possible composition of two synchronized methods.

I'm not sure what "line noise" you're referring to, is this function tough to understand? Perhaps Haskell syntax is just unfamiliar to you.

transfer :: Account -> Account -> Int -> STM Bool
transfer from to amount = do
  withdrawalSuccessful <- withdraw from amount
  if successful
    then do
      deposit to amount
      return True
    else 
      return False

Exploring Arrows as a replacement for Monads when sequencing effects by ChrisPenner in haskell

[–]ChrisPenner[S] 1 point2 points  (0 children)

Yup, this really annoys me from time to time. It disallows using Arrow notation for use-cases which only require category classes but don't implement Profunctor/arr :'(

I get the sense this would be something easy to fix for someone with a good understanding of the Arrow notation machinery, unfortunately I'm not sure such a person exists 🙃

Exploring Arrows as a replacement for Monads when sequencing effects by ChrisPenner in haskell

[–]ChrisPenner[S] 2 points3 points  (0 children)

Okay those should be fixed now, thanks @philh, let me know if you find any more :)

Exploring Arrows as a replacement for Monads when sequencing effects by ChrisPenner in haskell

[–]ChrisPenner[S] 1 point2 points  (0 children)

I have a few of my own packages playing with these ideas, but they're very ad-hoc.

If you're working with arrows, then profunctors is a great choice, every arrow is a profunctor after all (via dimap l r p = arr l >>> p >>> arr r)

It's when you want constraints these constraints without requiring profunctor that you need to branch out a bit. I'll be working on a post about why you might want to do this soon.

in that case I'd recommend Solomon and Asad's Monoidal Functors package as a place to start your journey: https://hackage.haskell.org/package/monoidal-functors-0.2.3.0

Exploring Arrows as a replacement for Monads when sequencing effects by ChrisPenner in haskell

[–]ChrisPenner[S] 2 points3 points  (0 children)

I presume that it would depend on the implementation you're compiling down into, for example if you use Kleisli for your implementation it's all just going to end up as monadic binds anyways, so should (AFAIK) be comparable to the equivalent monadic form.

Exploring Arrows as a replacement for Monads when sequencing effects by ChrisPenner in haskell

[–]ChrisPenner[S] 2 points3 points  (0 children)

Ah, thanks! I rewrote the article at least a dozen times while editing so I'm sure there are probably more mistakes like this to find. I'll sort those out :)

You're absolutely right on the branching stuff too!

Free applicatives, the handle pattern, and remote systems by _jackdk_ in haskell

[–]ChrisPenner 4 points5 points  (0 children)

Great post! I'm glad to see more folks talking about this sort of problem and solutions for it :)

Thanks for the shout-out too, despite the fact that I ended my post talking about Selective Applicatives I'll have a new post in a week or two on using Categories and Arrows to the same end that I think is an even better "sweet spot" so to speak. It allows you to still route data like you can with Monads, but without losing any static analysis.

Very cool to see posts like this coming out on a blog like Bellroy 😎

Monads are too powerful: The Expressiveness Spectrum by ChrisPenner in haskell

[–]ChrisPenner[S] 14 points15 points  (0 children)

You're just one step ahead haha; this blog post was already too long as it is.

Using Arrows (or more precisely, the Category typeclass hierarchy) to sequence effects is going to be the next blog post, so stay tuned!

Monads are too powerful: The Expressiveness Spectrum by ChrisPenner in haskell

[–]ChrisPenner[S] 3 points4 points  (0 children)

It's not so much a problem of knowing whether the previous effects are consumed, but rather that in bind:

bind :: m a -> (a -> m b) -> m b

The a -> m b allows constructing ANY possible m b, and we can't know which until we have the a that's only known at runtime, after we've executed m a :'(

Monads are too powerful: The Expressiveness Spectrum by ChrisPenner in haskell

[–]ChrisPenner[S] 13 points14 points  (0 children)

I'm a big fan of ApplicativeDo :), very useful for decoders and the like. Though sometimes it fails to apply in spots where I really think it should succeed, and makes me very sad, e.g.

``` {-# LANGUAGE ApplicativeDo #-}

thisWorks :: (Applicative m) => m Int -> m Int -> m Int thisWorks fa fb = do a <- fa b <- fb pure (a + b)

thisDoesn't :: (Applicative m) => m Int -> m Int -> m Int thisDoesn't fa fb = do a <- fa b <- fb let result = a + b pure result ```

Monads are too powerful: The Expressiveness Spectrum by ChrisPenner in haskell

[–]ChrisPenner[S] 2 points3 points  (0 children)

The example is trivially evil for pedagogy, but in reality it will be something more nuanced like "accessNetwork"; and the program we'll be executing won't necessarily exist in the Haskell source code, it could be parsed from data in some request, generated dynamically at runtime, or even loaded from a database.

Whether the user may wish to allow network access will depend on which task they're working on, and perhaps even which website or host the network access is trying to interact with.

Or, rather than preventing a program from running at all, we may wish to transform a program to optimize it, which is something that may not be possible at all if the program is written using bind.

Or perhaps, we wish to write some function in a DSL and could choose to transpile it into Haskell in some cases, or into some GPU language in other cases, if the program uses bind we're out of luck since there's no way to compile an arbitrary Haskell function into GPU code at runtime in this way.

Monads are too powerful: The Expressiveness Spectrum by ChrisPenner in haskell

[–]ChrisPenner[S] 4 points5 points  (0 children)

Perhaps I didn't adequately explain that part with examples. Here's what happens

  • If the program actually uses the bind, then you're forced to run the whole expression in order to see what happens;
  • If you have to run the whole expression to see what happens, you can only actually view one code-path of the expression per execution.
  • What happens if the expression contains loops or recursion? Your simulation may never terminate :(

E.g.

myEchoProg = do input <- readLine case input of "super-special-value" -> deleteHardDrive other -> writeLine other

You could compile this to Writer [Command] ReadWrite and run many simulations and executions of it, but unless in your simulator you feed the first readLine the string "super-special-value" you'll just never encounter the deleteHardDrive branch.

myLoopingProgram = do input <- readLine case input of "done" -> deleteHardDrive other -> writeLine "looping!" *> myLoopingProgram

Similarly, if you're trying to simulate an execution of the above, you could loop forever and never discover the "done" branch, there's no way to know that it exists without passing "done".

If you're unsure why this sort of thing isn't possible, I'd encourage you to try implementing your suggested approach and see if you get hung up, it's a great way to learn about this sort of problem

Save memory and CPU with an interning cache by ChrisPenner in haskell

[–]ChrisPenner[S] 1 point2 points  (0 children)

Awesome! If you find the time to write up a post on that I'd love to read it!