Open gelisam opened 11 years ago
I'm testing the default import of prelude unqualified. There are many problems with list functions that doesn't work on ByteString. For instance:
{-# LANGUAGE OverloadedStrings #-}
module Main where
import qualified Data.ByteString.Lazy.Char8 as C8
import Prelude
main :: IO ∅
main = do
let s = C8.pack "Foo bar"
let w = words s
print w
doesn't compile:
> ghc Main.hs
[1 of 1] Compiling Main ( Main.hs, Main.o )
Main.hs:10:19:
Couldn't match type `C8.ByteString' with `[Char]'
Expected type: String
Actual type: C8.ByteString
In the first argument of `words', namely `s'
In the expression: words s
In an equation for `w': w = words s
and we have the same problem in Hawk
:
> ps aux | hawk -m words
Won't compile:
Couldn't match type `Data.ByteString.Lazy.Internal.ByteString'
with `[GHC.Types.Char]'
Expected type: Data.ByteString.Lazy.Internal.ByteString
-> [GHC.Base.String]
Actual type: Data.ByteString.Lazy.Internal.ByteString
-> [Data.ByteString.Lazy.Internal.ByteString]
We should think really carefully what we want to import automatically because it can confuse the user. I don't think Prelude
is the right module, we should create something ad-hoc and different from Prelude
. For instance, ByteString
is not a Functor
while List
is a Functor
, so map
has a different meaning:
> import qualified Prelude as P
> import qualified Data.ByteString.Lazy.Char8 as C8
> :t Prelude.map
map :: (a -> b) -> [a] -> [b]
> :t C8.map
C8.map :: (P.Char -> P.Char) -> C8.ByteString -> C8.ByteString
> P.map isSpace "f oo"
[False,True,False,False]
> C8.map isSpace $ C8.pack "f oo"
<interactive>:20:8:
Couldn't match type `P.Bool' with `Char'
Expected type: Char -> Char
Actual type: Char -> P.Bool
In the first argument of `C8.map', namely `isSpace'
In the expression: C8.map isSpace
In the expression: C8.map isSpace $ C8.pack "foo bar foo bar"
Well, having map not work on lists isn't ideal either. Maybe this custom module could expose a two-parameter typeclass supporting both map :: (a -> b) -> [a] -> [b]
and map :: (Char -> Char) -> ByteString -> ByteString
? Something like the classy prelude.
Personally, for the first release, I would just qualify ByteString with B and remind the user that all string functions must be qualified with B. We could also have T for Data.Text.
What is least confusing for the user: that strings are instances of ByteString instead of String, or that prelude functions aren't available and/or don't have the types they do in the prelude? What is the least inconvenient, remembering to add the prefix B.
to all string functions, or remembering the intricacies of our custom functions?
I think the first answer to each question is slightly better, but I could easily be convinced otherwise.
I agree with you. What do you think if we close this and open a new issue about the default prelude.hs
to decide which modules should be imported and how?
This issue is about automatically importing the prelude if it does not appear in prelude.hs, just like the Prelude is implicitly available by default in a standard Haskell program unless you explicitly discard it or import it qualified. Whether the default contents of prelude.hs should import the Prelude or not (implicitly or explicitly) is indeed a different issue. Let's open a new one for choosing the default contents, but could we please leave this one open? I was very confused when my first attempt at creating a prelude.hs containing just "rev = reverse" failed to compile.
That's because of the NoImplicitPrelude
extension that I set on hint
. The idea was to give to the user the power to change everything, so if the user doesn't import Prelude
then he doesn't want it. That's probably different from what we want and it should be fixed.
I think we should import Prelude
every time as you said unless the user specifies the NoImplicitPrelude
extension. I leave this open, you are right.
p.s. now that we have the source parser we probably can extract the extensions and set them in hint
Letting the user decide if he wants NoImplicitPrelude? Good idea, I didn't think of that! Doing so in a more general way by accepting any extension and passing them along to hint? Absolutely brilliant!
I let you close the issue. As side note I kept the NoImplicitPrelude
extension and I import it manually if it is missing. I will remove the extension when we implement the system to load the user extensions.
If the user does not mention Prelude in ~/.hawk/prelude.hs, import it unqualified. If the user imports the Prelude unqualified, import it unqualified. If the user imports the Prelude qualified, import it with the same qualifier.