Two seemingly equivalent definitions; one compiles, the other doesn't [duplicate] - haskell

What exactly is going on with the following?
> let test = map show
> :t test
test :: [()] -> [String]
> :t (map show)
(map show) :: Show a => [a] -> [String]
I am wondering how I failed to notice this before? I actually encountered the problem with "map fromIntegral" rather than show - my code doesn't compile with the pointfree form, but works fine without eta reduction.
Is there a simple explanation of when eta reduction can change the meaning of Haskell code?

This is the monomorphism restriction, which applies when a binding doesn't take parameters and allows the binding to be shareable when it otherwise wouldn't be due to polymorphism, on the theory that if you don't give it a parameter you want to treat it as something "constant"-ish (hence shared). You can disable it in ghci with :set -XNoMonomorphismRestriction; this is often useful in ghci, where you often intend such expressions to be fully polymorphic. (In a Haskell source file, make the first line
{-# LANGUAGE NoMonomorphismRestriction #-}
instead.)

Related

CoercibleStrings toy GHC extension

I'm trying to write a small GHC extension to get my hands on GHC hacking. As suggested by the GHC gitlab wiki, I started off by having a look at this article by SPJ and SM to get a feel for GHC's architecture.
I now have an idea for what I think would be a relatively small GHC extension that I would like to have a crack at as a first attempt at actually modifying GHC's source code: CoercibleStrings. The outcome would be similar in spirit to that of the existing OverloadedStrings GHC extension, however, the latter I believe "only" infers the type (Data.String.IsString a) => a for a string literal l during type checking and then desugars them to fromString l, where fromString :: (Data.String.IsString a) => String -> a is from the typeclass Data.String.IsString.
This comes in handy when using libraries that make frequent use of alternatives to Haskell's standard String type, particularly if we frequently wish to pass in arguments represented as string literals, e.g. libraries used for terminal IO. For instance, consider the logging library simple-logger, which makes frequent use of Data.Text.Text, and contains the function logError :: (?callStack :: CallStack) => MonadIO m => Text -> m ().
Rather than writing something like:
import qualified Data.Text as T
import Control.Logger.Simple
...
logError . T.pack $ "Some error occured."
Using the OverloadedStrings GHC extension, the last line can be replaced with:
logError $ "Some error occured."
As Data.Text.Text is an instance of the typeclass IsString, this type checks and desugars to something like:
($) logError (fromString "Some error occured.")
This can result in fairly significantly less cluttered code due to these conversions being elided, however, in a fairly similar and common case, where operations applied to the string literal force it to be typed as a String, this benefit is lost. For example, consider:
import qualified Data.Text as T
import Control.Logger.Simple
...
logError . T.pack $ "Some error occured # " ++ (show locus)
Where locus is bound in the enclosing context and has a type that is an instance of the typeclass Show.
Now, due to the type of the concatenation operator, (++) :: [a] -> [a] -> [a], the result of evaluating the expression "Some error occured # " ++ (show locus) must be of type [a], where a is such that [a] is an instance of the typeclass IsString, i.e. String.
Therefore, in this case, the OverloadedStrings extensions does not help us. To remedy this, I propose the CoercibleStrings GHC extension, which would:
Allow the type checker to unify the type String with any instance of the typeclass IsString.
During desugaring, insert an application of the function fromString where these coercions are necessary.
Although the architecture of such an extension would be quite different from that of the OverloadedStrings extension, the idea seems quite straightforward, while providing, I would argue, a fairly large advantage over it. This brings me to the question of why it has yet to be implemented.
There seem to me to be a few potential problems with such an extension that I can think of. The first is that, given that String is an instance of the typeclass IsString, a type inference rule of the form
∀a ∈ IsString. Γ ⊢ e :: String => Γ ⊢ e :: a
can be applied to an expression of type String an unbounded number of times. So it may not be possible to modify GHC's type inference algorithm in such a way as to guarantee termination of type checking given the addition of such a rule. I think however that this issue can be prevented by requiring that a is not String and only allowing GHC to apply this rule if no other rules apply.
The second is from point 2) above, which is where to insert the applications of fromString. The simplest answer I guess would be to apply it to every expression of type String and given that the definition of fromString for String is simply id, any unnecessary applications would likely be optimised away. This may of course lengthen compilation times somewhat. I believe however that this could be targeted better given information from the type checker.
The third is that this type of implicit coercion goes against the philosophy of strongly typed languages such as Haskell, or indeed that these implicit coercions may introduce extra run-time costs that will be invisible to the programmer.
The second and third points are not existential issues that would prevent the extension from being written, however, I am unsure if the solution that I am suggesting to the first point actually works/is reasonably implementable within the existing GHC infrastructure/does not interact badly with other existing extensions.
I'd welcome any comment on these points or any pointers to up-to-date information on GHC's type checking algorithm. Thank you!

Haskell type inference for Functors

Lately I've been playing around with Haskell, and specifically the whole functors concept. The more I dive into it, the more a-ha moments I'm getting, and it certainly tickles my dopamine receptors quite a bit.
The problem I'm stuck with is the following. Here's the code that works, it lifts the function and then applies it first to the IO value, and then to a List.
replicator1 =
fmap (replicate 3)
replicator2 =
fmap (replicate 3)
main = do
replicated <- replicator1 getLine
print (replicator2 replicated)
It is very tempting to write it in a more concise manner, i.e.:
replicator =
fmap (replicate 3)
main = do
replicated <- replicator getLine
print (replicator replicated)
Part of me says it's conceptually right, since replicator should be applyable both to IO and to List instances, but being a strongly typed language Haskell doesn't allow me to do so. I think I pretty much understand why is this happening.
The question is: is there any way I can get any closer to the latter variant? Or is it fine to live with the former?
Thank you!
Your code is actually fine, except you ran into the dreaded monomorphism restriction which kept Haskell from inferring the most general possible type for replicator.
Essentially, with the restriction on, Haskell will not infer polymorphic types for bindings that do not look like functions. This means it has to pick a concrete functor like [] or IO for replicate and causes an error if you try to use it in two different contexts.
You can make your code work in three ways:
turn off the monomorphism restriction: add {-# LANGUAGE NoMonomorphismRestriction #-} at the top of your module.
make replicator look like a function:
replicator x = fmap (replicate 3) x
add explicit type signatures to your code
replicator :: Functor f => f a -> f [a]
replicator = fmap (replicate 3)
The third option is the most idiomatic. Good Haskell style involves adding explicit type signatures to all of your top-level identifiers. However, it's useful to know the other two options to understand what's going on and to be able to write quick-and-dirty throwaway scripts in Haskell without worrying about type signatures.

Haskell: Does ghci show "Chunk .. Empty"?

Learn You a Haskell has a code example like this:
ghci> B.pack [99,97,110]
Chunk "can" Empty
(B stands for Data.ByteString.Lazy)
But my ghci does not show Chunk and Empty data constructors.
> B.pack [99,97,110]
"can"
Did Haskell developers change the way the values of ByteString are printed?
Looks like Duncan added hand-written Show instance for lazy ByteString somewhere between 0.9.2.1 and 0.10.0.1. See http://hackage.haskell.org/packages/archive/bytestring/0.10.2.0/doc/html/src/Data-ByteString-Lazy-Internal.html#ByteString
Add: Here is the relevant patch
Old versions of BL.ByteString simple have a deriving Show in their data declaration. This results in the GHCi output as shown in LYAH, and ensures the output is valid Haskell code. The nice plain string "can" isn't really a valid Haskell representation of that bytestring – that is, not a valid Haskell 98 representation. However, it is common to use {-# LANGUAGE OverloadedStrings #-} in modules that use bytestrings, which makes it valid. Which is probably the reason that there is now (since 0.10) this nicer-to-read manual instance.

When can eta reduction change a function's type?

What exactly is going on with the following?
> let test = map show
> :t test
test :: [()] -> [String]
> :t (map show)
(map show) :: Show a => [a] -> [String]
I am wondering how I failed to notice this before? I actually encountered the problem with "map fromIntegral" rather than show - my code doesn't compile with the pointfree form, but works fine without eta reduction.
Is there a simple explanation of when eta reduction can change the meaning of Haskell code?
This is the monomorphism restriction, which applies when a binding doesn't take parameters and allows the binding to be shareable when it otherwise wouldn't be due to polymorphism, on the theory that if you don't give it a parameter you want to treat it as something "constant"-ish (hence shared). You can disable it in ghci with :set -XNoMonomorphismRestriction; this is often useful in ghci, where you often intend such expressions to be fully polymorphic. (In a Haskell source file, make the first line
{-# LANGUAGE NoMonomorphismRestriction #-}
instead.)

Type inference in GHCi vs. manual signature

when I type
:t map length . sum
into GHCi, it says that the type would be:
map length . sum :: Num [[a]] => [[[a]]] -> [Int]
However, if I create a file type-test.hs containing
x :: Num [[a]] => [[[a]]] -> [Int]
x = map length . sum
both ghc and ghci complain:
type-test.hs:1:1:
Non type-variable argument in the constraint: Num [[a]]
(Use -XFlexibleContexts to permit this)
In the type signature for `x': x :: Num [[a]] => [[[a]]] -> [Int]
Why does ghci allow me to infer the type for this (using :t), when FlexibleContexts are not enabled?
First, let's clarify one thing. What happens if we define the function in GHCi, rather than querying the type?
> let x = map length . sum :: (Num [[a]]) => [[[a]]] -> [Int]
<interactive>:0:9:
Non type-variable argument in the constraint: Num [[a]]
(Use -XFlexibleContexts to permit this)
In an expression type signature: Num [[a]] => [[[a]]] -> [Int]
And so on. In other words, the same thing. What if we let GHCi infer the type of the definition?
> let x = map length . sum
<interactive>:0:22:
No instance for (Num [[a0]])
arising from a use of `sum'
Possible fix: add an instance declaration for (Num [[a0]])
In the second argument of `(.)', namely `sum'
In the expression: map length . sum
This is roughly the same error that results from loading a file containing the definition without a type signature.
What's the upshot of all this? Well, think about the fact that it tells you what extension is needed. GHC is capable of recognizing what the type means, even if it rejects the type by default. I'd hardly expect GHC to use a completely different type checker depending on the combination of extensions used, so it seems easy to conclude that the offending type is being rejected for no reason other than the relevant extension being disabled.
The :t command in GHCi isn't part of the compilation process--it's a hotline to the type checking and inference system, letting you ask the type of hypothetical code. There's no obvious reason for it to restrict itself arbitrarily based on extensions, when a more general type could still be informative, for the same reason that the error messages above tell you use -XFlexibleContexts to permit this rather than merely syntax error in type constraint.
As a possibly more interesting aside, there are also cases where an inferred type will be accepted happily by the compiler, but the inferred type can't actually be written out explicitly for one of several reasons.
For instance, disabling the monomorphism restriction will allow your example to have its type inferred (matching what :t says), despite that type requiring an extension to write manually.
Another example are definitions in the where clause of a function definition which make use of polymorphic arguments to the parent function. Their own types are not polymorphic, being dictated by the arguments received in the outer scope, yet the type variables in the parent function's signature are not in scope for the where clause¹. There may be other examples as well.
¹ The type variables from the parent's signature can be brought into scope with the ScopedTypeVariables extension and an explicit forall, if that's needed.
(This doesn't answer your original question, but solves the problem with the code instead)
These errors hint at that your code as written probably isn't what you meant. This code:
map length . sum
means "Take my list of numbers, and sum it, then compute the length of each element(??) of the resulting number." That makes no sense.
You probably meant:
sum . map length
which means "Take my list of lists, compute the length of every element, and sum the lengths."
What the error message itself means is that since sum returns a number, aka the type of sum is Num n => [n] -> n, and you then try to use map length on that, which has type Num m => [[a]] -> [m], the compiler tries to say that n == [[a]] to make the types match, and everything goes downhill from there.
Another way to look at it:
Haskell tells you that your expression had a valid type, namely [[[a]]] -> [Int] if only [[a]] would be an instance of Num.
This is like some girls telling you she will go out with you when hell freezes over. You wouldn't complain that she promised to go out with me despite the hell can freeze over possibility is not given in this world, would you? You would rather notice that she just said No in a more or less polite way.
The problem is that the function is perfectly acceptable, but its most general type is not allowed to be specified by the language grammar. What could ghci do in such circumstances? I can only think of two options, either give a type that can only be specified with some extension enabled, or giving an error. Giving an error without mentioning that it can be fixed by enabling an extension seems not very desirable to me. Just having :t report the inferred most general type is simpler, and quite possibly when the feature was implemented, nobody thought of situations like this.
See http://haskell.org/ghc/docs/6.12.1/html/users_guide/interactive-evaluation.html#extended-default-rules

Resources