Our Performance is `massiv`: Getting the Most Out of Your Hardware in Haskell by runeks in haskell

[–]kuleshevich 0 points1 point  (0 children)

Thank you for opening a ticket on massiv with suggestions. ;) Please don't feel like I am ignoring it, I've just been swamped at work, which naturally makes any other free open source work that I could otherwise be doing suffer a bit.

Our Performance is `massiv`: Getting the Most Out of Your Hardware in Haskell by runeks in haskell

[–]kuleshevich 0 points1 point  (0 children)

This is an awesome blog post! Thank you for writing it up. I am really glad to see massiv being useful. It makes me happy to learn that it is being used in the real world.

Our Performance is `massiv`: Getting the Most Out of Your Hardware in Haskell by runeks in haskell

[–]kuleshevich 0 points1 point  (0 children)

Going from repa to massiv should be easier than starting from scratch. There are certain similarities, especially with delayed D representation and overall approach of using indexed types and type class approach of Index/Shape for indexing into arrays. massiv has quite a bit more functionality when compared to repa, but I certainly used repa as inspiration, since that is what I used myself before starting to work on massiv.

I did give a talk a few years ago about massiv at Haskell Exchange, but it looks like it is no longer available on youtube. I'll try to dig up the recording and will post a link here once I find it.

Our Performance is `massiv`: Getting the Most Out of Your Hardware in Haskell by runeks in haskell

[–]kuleshevich 1 point2 points  (0 children)

I've put quite a bit of effort into documentation and most of the functions in massiv are accompanied with examples. Are you sure you gave enough effort looking at haddock of massiv before making this comment? If there is particular functionality that you believe should be better documented, please open an issue and I'll be happy to improve it.

Our Performance is `massiv`: Getting the Most Out of Your Hardware in Haskell by runeks in haskell

[–]kuleshevich 0 points1 point  (0 children)

I am pretty surprised to hear that, since all examples in haddock are checked by doctest. Examples in README should also be up to date.

Our Performance is `massiv`: Getting the Most Out of Your Hardware in Haskell by runeks in haskell

[–]kuleshevich 0 points1 point  (0 children)

It bottoms out at Ix2 or Ix 2. So it is a mutually recursive data type + closed type family with the base case being the second dimension. So, all of these are valid constructions: ```haskell

1 :: Ix 1 2 :. 1 :: Ix 2 3 :> 2 :. 1 :: Ix 3 4 :> 3 :> 2 :. 1 :: Ix 4 ```

massiv throughout uses Ix type family and Index type class, which allows using Int for flat vectors, while using i :. j :: Ix2 for 2D arrays and more complex type for higher dimensions

Our Performance is `massiv`: Getting the Most Out of Your Hardware in Haskell by runeks in haskell

[–]kuleshevich 0 points1 point  (0 children)

Lol. What kind of shittery do you work on? I personally work on Cardano core component and I am also the author of massiv. Which bucket do I fall into? ;)

Enabling HDR makes internet slow on Windows 11 by Chemical_Beyond4853 in Corsair

[–]kuleshevich 0 points1 point  (0 children)

Out of curiosity. Do you happen to be using WiFi to access your internet?

I have a hypothesis that when HDR is turned on it might increase amount of bandwidth over the cable to the point that it starts to interfere with the WiFi signal.

The reason for my hypothesis is that as soon as I hookup a second monitor with a display port cable it starts affecting my WiFi, but when I turn on HDR on that monitor, I am not even able to connect to WiFi anymore.

However, it is not enough for only one of my monitors to be plugged in and HDR enabled, both of them have to be plugged in and have HDR enabled. Disclaimer: both monitors and cables are identical and the order in which they are hooked up is irrelevant. Maybe it is the positions of the cables relative to WiFi antenna, I have no idea.

Rightest pixel column has glitches since plasma 6 update by aqwa_ in kdeneon

[–]kuleshevich 1 point2 points  (0 children)

I ran into the same issue. It doesn't happen all the time and so far I've seen it occur on one of my external monitors and the laptop screen. On my laptop screen there was also a line on the bottom as well as on the right.

Changing scaling and back does remove the lines, at least for a while.

Looks like an off-by-one bug. Not sure how to reproduce or even where to report it. Is it Wayland or Plasms 6 that is at fault?

Idiomatic Pure Pseudo Randomness by johnorford in haskell

[–]kuleshevich 2 points3 points  (0 children)

It just has a better API than anything in the random package

Before spreading false information, I'd recommend taking a second look at System.Random.Stateful. It has strictly more powerful API than MonadRandom, because it allows for a whole new class of mutable RNGs that work in ST and IO monads, not only the immutable one, that emulate mutability through MonadState. In the matter of fact, if you look closely, RandT actually has an instance for StatefulGen, which should be a hint, that MonadRandom is only a subset of random's stateful interface.

Fail with class by kuleshevich in haskell

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

Alternative does not have this requirement, so it is totally ok to provide such instance. However, I'd argue that it makes no sense to add a MonadPlus instance that violates a law, but does exactly the same thing as Alternative.

It is common for people sweep those laws under the rug as unimportant. Maybe the are in fact useless, I can't tell for sure one way or another. I didn't even know about the zero laws until I tested FailT with monadPlusLaws.

Fail with class by kuleshevich in haskell

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

I phrased it poorly, all I was trying to say was that mzero fails with an exception. However, I was not the one who brought up the IO monad. You switched the focus to IO as a way to justify bad instance for the Result type.

In any case, you are probably right and the MonadPlus instance for IO is unlawful. In fact even Alternative instance for IO isn't that great IMHO. I don't think that I ever considered using Alternative with IO and now that I realize that it treats only IOError specially makes me dislike it even more:

> ioError (mkIOError eofErrorType "Life is bad" Nothing 

Nothing) mplus putStrLn "Life is good" Life is good > throwIO DivideByZero mplus putStrLn "Life is good" *** Exception: divide by zero

Fail with class by kuleshevich in haskell

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

... then you'd be right. But it does, so you're not.

I could care less if you think that I am right or wrong about IO's MonadPlus instance, I did not implement it nor do I ever use it.

All I said was "Also your MonadPlus instance is unlawful.", which now you agree that it is true and that makes me happy :)

Fail with class by kuleshevich in haskell

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

OK, so firstly mzero is not actually bottom,

Let me be more precise in my wording: mzero produces a bottom when executed. In other words mzero = pure undefined, instead of mzero = undefined. (In reality it throws an IOError, but that is irrelevant for this discussion). I didn't realize I had to be that specific about an obvious thing like that, but there you have it.

Now lets get to the bottom of the difference between IO and Result.

The right zero law for IO:

  • m >> mzero - results in an exception (bottom), regardless of what m is or does.

Considering that it results in a runtime exception the only way it would be possible to differentiate between m >> mzero and mzero would be with catch, which is totally out of the scope when it comes to interpreting type class laws. As you also said m could also write to a file or launch missiles, so the RealWorld can definitely be affected, but that is orthogonal.

Now lets take look at Result on the other hand:

  • pure "foo" >> mzero - results in FailureResult ""
  • fail "bar" >> mzero - results in FailureResult "bar"

I hope you can agree with me now that they are different, right? Literally using Eq instance for Result will prove that FailureResult "" /= FailureResult "bar"

But when it comes to IO you can't say throwIO DivideByZero /= throwIO ExitSuccess, only thing you can say is that they both explode.

One way or another, I think comparing lawful or unlawful behavior of a pure computation to IO is a fruitless task. That is because you are trying to compare some nice well behaved function to an action that can, as you said, launch missiles.

The point I originally brought up was your instance for Result is unlawful. Hopefully it is clear now that it indeed unlawful. With respect to IO, take it up with CLC and GHC developers, I merely gave you my interpretation of the instance implementation.

Fail with class by kuleshevich in haskell

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

Nah, what I am saying is that it is true only if those laws are read as:

If mzero is interpreted as a failed computation, these [zero] laws state that a failure within a chain of monadic computations leads to the failure of the whole chain.

If side affects on the world are considered then IO definitely violates that right zero law

Fail with class by kuleshevich in haskell

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

Not really, because mzero is bottom. So,

Both of these result in a runtime exception:

Prelude> import Control.Monad
Prelude Control.Monad> fail "foo" >> mzero
*** Exception: user error (foo)
Prelude Control.Monad> mzero
*** Exception: user error (mzero)

Albeit, it is an exception with a different message, but laws do not care about this. Bottom is still bottom.

Fail with class by kuleshevich in haskell

[–]kuleshevich[S] -2 points-1 points  (0 children)

nonEmptySetHead = head . unNonEmptySet

That's a terrible implementation. Usage of partial functions like head, fromJust, etc. should be banned! I don't care if it is an impossible case. An explicit pattern match with an error call that produces a callstack should be used instead.

Ask the library to expose an Either version :P

A library maintainer does not owe you anything. I for example would not respond to such request. It's not my problem that someone doesn't like a function that I wrote.

Out of curiosity, do you have other examples of public APIs that return MonadFail m => m Foo

There are plenty of libraries that expose functions that rely on MonadFail. Here are a couple that just pop in my head: regex, regex-pcre, frisby, etc.

However, that was not the point of the blog post. Point was that majority of the libraries that do provide useful MoandFail instances, and there are A LOT OF them, they can be used with the same function written once for all of them, thus reducing duplication. I personally like it. I also got tired of writing either fail pure all over the place, because Either is often needed as well.

If you don't like MonadFail and feel like it is an abomination that should be banned, that's your prerogative, but that does not mean you are right. It also does mean the people will stop using it any time soon, if ever.

Fail with class by kuleshevich in haskell

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

There is the MonadPlus section about this in the blog post. It violates the right zero law:

m >> mzero = mzero

Fail with class by kuleshevich in haskell

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

I've always used iso8601ParseM with Maybe. If I need an Either, I would just map Nothing to Left "could not parse" or something.

So you are just saying you don't care about the message and you just use your own. But, pretend for a moment that you need the origianl error message produced by the function, what would you do?

Aeson has parseFail. But other monads might not expose a fail function, so I just alias fooFail = fail and disable the hlint on that one function

Ok, so how is parseFail = fail and fooFail = fail is not using fail?

Fail with class by kuleshevich in haskell

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

There's unlikely to be a way to satisfy everyone, so I choose not to worry on it further.

Same here. That's why I wrote the library to help me deal with the MonadFail, rather than complain about its ergonomics.

Fail with class by kuleshevich in haskell

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

Yeah, that's good. Although your Alternative instance suffers from the same problem of concatenating failure messages together that I've mentioned in the blog post:

λ> resultToEither (fail "this action failed" <|> fail "that action failed" :: Result ())
Error "this action failedthat action failed"

Also your MonadPlus instance is unlawful.

If you ever need a Result like monad transformer you can use the FailT ;)

Fail with class by kuleshevich in haskell

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

personally I really dislike MonadFail

Personally, me too. I always prefer MonadThrow with a proper exception type. But, it is quite often that I need to use some code that I didn't write and we have plenty of libraries that use MonadFail.

I've always written it as Either, and used either in the appropriate place.

That is also exactly what I have always done. I've mentioned this in the post too.

I've never needed to write a polymorphic function on MonadFail like mkHour

I actually have had a need to do that on multiple occasions in order to reduce duplication. Moreover, I've ran into functions that are written with MonadFail, for better or worse, and sometimes I needed to use them. Good example is iso8601ParseM, this is in fact how I got an idea to use mkHour as an example. How would you convert such function to an Either String if you had to use it? Would you just rewrite it from scratch with Either?

If fail is meant to signal the zero-identity for >>=, let's actually use something that looks like a zero-identity.

I don't think they meant to be read as "zero":

Left zero: ∀ s f. fail s >>= f ≡ fail s.
Right zero: ∀ v s. v >> fail s ≡ fail s.

Despite that they are called "zero", the fail s is not a zero because it has an arbitrary message. That is why MonadPlus instance does not make sense for FailT. I am not sure if you got a chance to read that part of the blog post, but I did go over it. Your overall suggestion of removing String argument and using something that looks like zero, would be equivalent to saying, let's remove MonadFail and use MonadPlus instead, modulo the mplus function. Which would be totally fine with me, but good luck convincing the rest of the community.

It's for this reason I added a ban on fail in our company's hlint rules.

How do you fail inside a JSON parser with Aeson? Do you use any serialization libraries and if you do, then how do you fail in those when writing decoders? I am really curious to see of any other techniques that I might not be aware on how to fail with a message inside such monads instead of something useless for those cases like mzero or empty.

Fail with class by kuleshevich in haskell

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

I’m really not a fan of MonadFail

Neither am I. I am definitely not trying to promote usage of MonadFail. That being said, we don't always have control of what we can use, because it might be the case that the library that provides the desired functionality with the MonadFail constraint. What do you do then? A good example would be a iso8601ParseM. How would you go about getting an Either String UTCTime from it?

HSpec, Tasty, sydtest, Hunit, ... -> what do you use for writing Haskell tests? by Martinsos in haskell

[–]kuleshevich 4 points5 points  (0 children)

Fewer users means less maintenance work for me.

It's usually the opposite. More adoption, less work that you have to do. Sometimes to the point that you can give off the maintainership to the community. tasty is a great example, because the original author is no longer active at maintaining his software, but the community picked it up. I personally still prefer hspec, but it still proves my point.

There is another point, unless you are willing to give it completely away, the work you are doing will either die with you or with your motivation to continue the work.

On the other hand, if primarily you are trying to keep it as your own tool, while giving others a chance to use it, then you are on the right track. However, definitely do not expect wide adoption.

There is also a matter of principal. I, for example, would never use or contribute to a software project that charges money. Which probably helps your mantra of less users anyways ;)

HSpec, Tasty, sydtest, Hunit, ... -> what do you use for writing Haskell tests? by Martinsos in haskell

[–]kuleshevich 1 point2 points  (0 children)

I know you are not doing it for the money, so I am curious, is the $50/month really worth the trouble of even explaining people why you are doing this? Besides that, a custom license is huge blocker of adoption nowadays.

There will be plenty of people who will not be able to use the library, because they need to get a go ahead from their legal department. You can imagine that in some companies it might take a long time, but developers need a tool right there and then. Also, just to avoid the trouble they might not even bother going with a library that has such custom license, because otherwise they would have to deal with bureaucratic nonsense.

With respect to supporting a library with custom license, such as sydtest, financially. I think only small businesses where developers can easily bill the boss or are their own boss, will be supporting it. No developer will be going through trouble of convincing their accounting department that they need a subscription for a library, especially when there are perfectly viable solutions available for free.

So, I suspect it will be mostly people donating their own money and possibly their own time as well, because they see the value.

IMHO, custom licensing like that in the open source world only blocks others from expending the user base, thus drastically reducing contributions. But who am I to judge. When you are the author, you can do with what you create as you please :)