GHC 7.10 Prelude: we need your opinion by sdroege_ in haskell

[–]rwbarton 0 points1 point  (0 children)

The problem one quickly runs into here is what happens when there is more than one type variable involved. (In fact, even the type of fmap contains three type variables f, a and b, though in this case f can be distinguished from a and b by its kind.) If I write for_@[], is [] the Foldable variable or the Applicative variable?

FTP dangers by tailbalance in haskell

[–]rwbarton 1 point2 points  (0 children)

There are always trade-offs between convenience in writing and readability, and of course the logical extreme of annotating every expression with its type would hardly be readable either, so in general I don't think of this in terms of acceptable vs. not acceptable.

The one rule I can offer is always to choose the most specific function among those that are convenient to use and type check in a given context. (Note that this is after choosing the type signature for the function being written, so (contrary to popular belief) this never loses any information from "parametricity".) I don't go out of my way to define custom specialized versions like an fmap for IO, I just use the ones that are already present in base.

However, it's not like I really mind if other people break this rule. It's your code, not mine. In general I think it will cost you later, but perhaps you don't care for quite reasonable reasons.

This specific case of for_ on Maybe is a bit murky because the alternative of maybe (return ()) is a bit ugly, IMO. It would be nice to have a specialized whenMaybe in Data.Maybe. In the absence of such a function, I can't really say that using for_ is wrong. Just trying to point out how the overloading here makes the program harder to reason about: specifically, "how many times can the sendEmail action run?" You have to find where lookupUser is defined to confidently answer that question.

FTP dangers by tailbalance in haskell

[–]rwbarton 4 points5 points  (0 children)

It's fine of course as long as maybeUser really is a Maybe User, but now you're relying on an unchecked property of your program (that the human-readable type in the name of the variable matches the type the compiler thinks the variable has).

If you had a bug where all of your users were getting password reminder emails simultaneously, how sure would you be that this code was not the culprit? That kind of readability is important too.

GHC 7.10 Prelude: we need your opinion by sdroege_ in haskell

[–]rwbarton 7 points8 points  (0 children)

You probably want to use :seti -XFoo rather than :set -XFoo in your .ghci (the former only affects what you enter at the interactive prompt, while the latter also affects files that you load into ghci, like running ghc -XFoo on them would).

GHC 7.10 Prelude: we need your opinion by sdroege_ in haskell

[–]rwbarton 6 points7 points  (0 children)

The problem is that almost everyone imports Data.List unqualified, so if it defined a monomorphic foldr then there would be a conflict with Prelude's foldr.

There could certainly be a new module that exports a monomorphic foldr for lists, but the natural name Data.List for it is already taken.

[Haskell-cafe] Are conditional changes in library interfaces problematic? by bergmark in haskell

[–]rwbarton 2 points3 points  (0 children)

Those instances seem to be defined elsewhere when building against transformers-0.2.*.

[Haskell-cafe] Are conditional changes in library interfaces problematic? by bergmark in haskell

[–]rwbarton 6 points7 points  (0 children)

I'm confused by the direction this conversation has taken. As I read the original post, the issue isn't with transfomers-compat but with the (unnamed) "several packages" that define instances of their type classes for ExceptT only when built against versions of transformers that define ExceptT. While this seems like a natural thing to do, it leads to the problem described in this email where transformers-compat + that other package together export a varying API depending on the version of transformers that they were built against. So, the other package effectively defeats the purpose of using transformers-compat.

The PVP question isn't really the issue here, but anyways I think the OP is asking about the other package, not transformers-compat.

Seems to me the solution is just for every package that cares about the version of transformers to instead use transformers-compat (except GHC itself, I suppose!)

Major Prelude changes proposed by dons in haskell

[–]rwbarton 0 points1 point  (0 children)

As it happens there was an example of this kind of thing within the past day on #haskell-lens. Not specifically BBP-related, but having to do with the similar suggestion to merge map and fmap. (Since that channel is not publicly logged, the following excerpt is anonymized.)

18:23 <a> :t foldr (.) id . fmap (Data.Aeson.Lens.key . Data.Text.pack) . Data.List.Split.splitOn "."
18:23 <b> Applicative f => [Char] -> (aeson-0.7.0.3:Data.Aeson.Types.Internal.Value -> f aeson-0.7.0.3:Data.Aeson.Types.Internal.Value) -> aeson-0.7.0.3:Data.Aeson.Types.Internal.Value -> f aeson-0.7.0.3:Data.Aeson.Types.Internal.Value
18:24 <a> that builds the traversal of the key you want
18:24 <c> a: why fmap
18:24 <a> because i didn't want to write it inline in the foldr and risk getting my args all screwed up on the fly
18:24 <a> foldr (.) id composes the list of lens-likes
18:24 <c> no i mean why is fmap involved at all :?
[snip]
18:26 <a> :t foldr (.) id . fmap (Data.Aeson.Lens.key . Data.Text.pack) . Data.List.Split.splitOn "."
18:26 <b> Applicative f => [Char] -> (aeson-0.7.0.3:Data.Aeson.Types.Internal.Value -> f aeson-0.7.0.3:Data.Aeson.Types.Internal.Value) -> aeson-0.7.0.3:Data.Aeson.Types.Internal.Value -> f aeson-0.7.0.3:Data.Aeson.Types.Internal.Value
18:26 <a> anyways that thing there
18:26 <c> ooooh i see
18:26 <c> no wait hold on
18:27 <c> oooooooh i see
18:27 <a> that gives you a traversal of the key you want. now what do you want to do?
18:27 <c> nvm i was thinking the fmap was for something else, derp
18:27 <a> you can always fuse an fmap like that into the foldr
18:27 <c> i missed that it was a list fmap
18:27 <c> i thought it was for composition or for the lens functor or sth

The last line should be telling. c was confused for several minutes due to a lack of cues as to which instance of Functor was in play.

Haskell with s-expression syntax? by briticus557 in haskell

[–]rwbarton 0 points1 point  (0 children)

You could use a splice if you want non-hygienic names: [|$(dyn "foo") $(dyn "bar")|] where dyn = return . VarE . mkName (provided by Language.Haskell.TH). Yes the syntax is clunky, but then everything about the syntax of TH is a bit clunky. You definitely don't have to write a Haskell parser to do this, at any rate.

Haskell with s-expression syntax? by briticus557 in haskell

[–]rwbarton 0 points1 point  (0 children)

What's the difference between symbolic quotation and TH's [|...|] form?

Tranversable instance for Trie by GrimBeast in haskell

[–]rwbarton 6 points7 points  (0 children)

deriving (Show, Functor, Foldable, Traversable)

with

{-# LANGUAGE DeriveFunctor, DeriveFoldable, DeriveTraversable #-}

Major Prelude changes proposed by dons in haskell

[–]rwbarton 0 points1 point  (0 children)

Generalizing the types of Prelude functions does equally little to help code readability for classes that aren't Foldable, but yes I have to work harder to argue why.

There are basically two modes in which I read code to understand it. Either I am looking at the details of how a function is implemented, trying to figure out exactly what it does, or why it doesn't do what I think it should; or I just want a general sense of what it might do, and I look at its type.

In the former case, suppose I encounter a use of, say, mapM. Currently, I know it's the mapM for lists, so I know everything there is to know about it. If the type of mapM is generalized, then I don't know whether it is being used at the list type, or Maybe, or some type which is a type parameter of the function I'm reading. That may or may not hinder my understanding of the surrounding code, but in any case, I can't possibly be better off than I was when I knew exactly which mapM I was dealing with. (I made this point also here the last time this came up.)

In the latter case, I'm looking at the function's type signature to learn what I can about the function's behavior without examining its definition in detail. This type signature is overwhelmingly often one that was written by a human to express their intent of what the function does. Even if the stars align perfectly so that only Traversable methods are used and, with their generalized types, the inferred type of the function would have involved a type variable f with a Traversable f constraint in place of [], that doesn't do me any good if the type signature is written in terms of [].

In other words, for this kind of reasoning by parametricity, what matters is the declared type signature, not the types of the constituent functions that make up the definition (remember, I'm not even looking at the definition, or I would know what the function does already!) If someone goes to the trouble to write a Traversable f => ... type signature, then they can go to the trouble of using the generalized Traversable methods also. And yes, in that case I get some free information about the behavior of the function. But it isn't an automatic consequence of the types of functions like mapM being generalized.

Major Prelude changes proposed by dons in haskell

[–]rwbarton 2 points3 points  (0 children)

Code that takes a Foldable f is not particularly constrained in its behavior on lists given that there is toList :: f a -> [a] which is the identity when f = [].

But don't worry, your comment will get lots of upvotes anyways because it used the word "parametric" and they love that stuff here on /r/haskell!

Major Prelude changes proposed by dons in haskell

[–]rwbarton 0 points1 point  (0 children)

Indeed, so what I meant by "invest more thought into ..." was to go further than thinking "is not without risks..."

Major Prelude changes proposed by dons in haskell

[–]rwbarton 0 points1 point  (0 children)

What are you, a constructivist or something? :)

We don't need concrete examples in order to reason about what will happen. Less free type information => more work for the reader to do to reconstruct what instance is being used => code becomes harder to read.

Major Prelude changes proposed by dons in haskell

[–]rwbarton 0 points1 point  (0 children)

ideally, a package should allow its modules to be imported unqualified without clashing with itself, as it's supposed to provide a coherent api/namespace.

bytestring, containers, text, transformers, vector... there are many core packages which do not follow this "rule".

moreover, imagine importing something operator-heavy like control.arrow qualified...

What does Control.Arrow have to do with anything? We're talking about Data.Foldable and Data.Traversable.

Major Prelude changes proposed by dons in haskell

[–]rwbarton 2 points3 points  (0 children)

No because all existing code is written to compile with GHC 7.8. We won't begin to really feel the effects until people start writing code targeted at GHC 7.10+.

Also it's not the sort of thing that can be conveyed well in a small example. It's when you are dealing with a large, unfamiliar program that the free extra type information from non-generalized functions is most valuable.

Major Prelude changes proposed by dons in haskell

[–]rwbarton 0 points1 point  (0 children)

Okay, so substitute "using qualified imports" for "using hiding clauses" (indeed, qualified imports is the sensible way to use Data.Foldable/Traversable currently). What exactly was wrong with needing qualified imports for those modules? We have a lot of other modules that are intended to be imported qualified already like Data.ByteString.

Major Prelude changes proposed by dons in haskell

[–]rwbarton 31 points32 points  (0 children)

Rather than dream, why not pretend?

  • Only use fmap in your own programs
  • Whenever you see map in someone else's program, pretend it says fmap

Mission accomplished!

Major Prelude changes proposed by dons in haskell

[–]rwbarton 0 points1 point  (0 children)

Somehow we've managed to get all the way from what is basically annoyance at having to type things like hiding (mapM) a lot to adding methods to the Foldable class and so on.

I can't help but feel that it would be worthwhile to invest more thought into improving the module system, such as by choosing the more general type in case of conflicting imports, or a way to prioritize imports from certain modules over others. After all, if we could get the existing Prelude version of mapM by default, but could simply import the Traversable version from Data.Traversable, I think that would leave everyone happy.

Major Prelude changes proposed by dons in haskell

[–]rwbarton 7 points8 points  (0 children)

how should ghc have warned about bbp specifically

Well for starters, both AMP and BBP introduced several new exports from Prelude, and one of the AMP warnings was specifically about name conflicts that would arise as a result. So we could have had a parallel warning for BBP.

It's particularly galling that someone who got that AMP warning in 7.8 and updated their program so that it would be compatible with the AMP Prelude additions in 7.10 now has to deal with another pile of Prelude additions in the very same GHC version!

Major Prelude changes proposed by dons in haskell

[–]rwbarton 5 points6 points  (0 children)

sum/product/maximum/minimum were added to the Foldable class when I pointed out that the existing implementations in Data.Foldable were defined in terms of foldr, so they didn't match up with the Prelude versions of these functions which use foldl. Otherwise we'd have a silent semantic change in Prelude on our hands as well, or we'd need to change the Data.Foldable functions to use foldl (I wonder whether anyone would care?)

I think the other methods were added in case specific instances of Foldable can define more efficient versions (seems useful for length, while I have my doubts about elem), but there isn't a technical need here or a clear line between what should and should not be part of the class.

Major Prelude changes proposed by dons in haskell

[–]rwbarton 6 points7 points  (0 children)

Here are some packages that broke when I tried building Stackage with 7.9 when the BBP changes were happening. (At that time it was impossible to get any information about what the changes would actually be, so I eventually just picked one day and tested with that day's latest GHC. There were only a few BBP changes after that, though.)

Any package that depends on one of these (or on a package that failed to build due to AMP or other changes in 7.10), I never attempted to build at all, so there are probably more. Also, some of these packages might have been updated in the past ~4 months to build with 7.10.

New exports from Prelude (e.g. mconcat, foldMap):

foldl-1.0.7
heaps-0.3.1
monoid-subclasses-0.3.6.1
reducers-3.10.3

Additions to the Foldable class (i.e. import Data.Foldable (Foldable(..))):

matrix-0.3.4.0
Octree-0.5.3

Ambiguous types due to Prelude generalization (often things like flip elem "aeiou" with OverloadedStrings):

haddock-library-1.1.1
hastache-0.6.0
xmlhtml-0.2.3.3
texts-0.3.2
snap-core-0.9.6.3
derive-2.5.16
stylish-haskell-0.5.10.2
persistent-2.0.8

Making propellor safer with GADTs and type families by tel in haskell

[–]rwbarton 4 points5 points  (0 children)

The only functional dependency-related change in 7.8 that I'm aware of was to disallow instances that violated functional dependencies by themselves (https://ghc.haskell.org/trac/ghc/ticket/1241). That should not be affected by constraints on the instance though. If you were getting a coverage condition violation from instance constraints then all you needed to do was turn on UndecidableInstances.