ssadler / hawk

Awk for Hoodlums
BSD 3-Clause "New" or "Revised" License
35 stars 2 forks source link

import Prelude by default #50

Open gelisam opened 11 years ago

gelisam commented 11 years ago

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.

melrief commented 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"
gelisam commented 11 years ago

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.

melrief commented 11 years ago

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?

gelisam commented 11 years ago

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.

melrief commented 11 years ago

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

gelisam commented 11 years ago

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!

melrief commented 11 years ago

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.