Правильный способ импорта вещей, уже определенных в Prelude в Haskell

Я пытаюсь определить экземпляр Foldable в Haskell, и у меня есть некоторые проблемы с импортом.

Итак, сначала попробуйте:   модуль MyList   где

import Data.Foldable

data MyList a = MyList [a]

instance Foldable (MyList) where
  foldr f b (MyList as) = foldr f b as

Результат (нормальный, но раздражающий)

Неоднозначное появление `foldr '

Итак, я думаю, мне нужно скрыть это от Prelude:   модуль MyList   где

import Prelude hiding (foldr)
import Data.Foldable

data MyList a = MyList [a]

instance Foldable (MyList) where
  foldr f b (MyList as) = foldr f b as

Эта компиляция, я загружаю в ghci и пробую некоторые основные вещи:

*MyList> foldr (:) "" (MyList "hello")
"hello"
*MyList> foldl (flip (:)) "" (MyList "hello")

<interactive>:1:0:
    Ambiguous occurrence `foldl'
    It could refer to either `Prelude.foldl', imported from Prelude at MyList.hs:4:0-28
                          or `Data.Foldable.foldl', imported from Data.Foldable at MyList.hs:5:0-19
*MyList>

Итак, foldr работает, но foldl не делает. Мой первый вопрос:

Должен ли я скрывать вручную каждый метод, определенный в Data.Foldable из Prelude - их хороший способ сделать это?

.

Чтобы избежать этой проблемы, я попытался выполнить квалифицированный импорт:   модуль MyList   где

import qualified  Data.Foldable as F

data MyList a = MyList [a]

instance F.Foldable (MyList) where
  foldr f b (MyList as) = foldr f b as

Кажется, компилируется в ghc, но

*MyList> foldr (:) "" (MyList "hello")

<interactive>:1:14:
    Couldn't match expected type `[Char]'
           against inferred type `MyList Char'
    In the third argument of `foldr', namely `(MyList "hello")'
    In the expression: foldr (:) "" (MyList "hello")
    In the definition of `it': it = foldr (:) "" (MyList "hello")

foldr не найден, но suprisingly работает F.foldr в ghci.

*MyList> F.foldr (:) "" (MyList "hello")
"hello"

Но только в ghci, если я пытаюсь импортировать MyList в файл, foldr, F.foldr, MyList.F.foldr и MyList.foldr не работают.

Почему это работает в ghci, но не в реальном?

Думаю, мне нужно снова импортировать Data.Foldable(и снова в каждом файле с помощью MyList)

Есть ли лучший способ сделать это (например, экспортировать Data.Foldable в MyList)?

(Я новичок в Haskell и особенно с модулями)


После нескольких ответов, похоже, нет чистого решения этой проблемы. Тем не менее, я уверен, что я не первый, что сделал, поэтому

Какова распространенная практика для решения этой проблемы?

Спасибо за помощь.

Ответы

Ответ 1

Здесь вариант решения Томаса Эдинга, с меньшим набором текста. В принципе, вы можете импортировать (в Prelude) Prelude, скрывающие некоторые функции, а затем повторно экспортировать Prelude; это будет экспортировать Prelude за вычетом этих функций. Этот метод обычно полезен для написания модуля frontend, который реэкспортирует только некоторые функции в библиотеке.

Затем вы можете повторно экспортировать также Data.Foldable.

Где я заменил некоторые функции Prelude эквивалентами (примеры для Foldable и Category).

module Prelude2 (module Prelude, module Data.Foldable, module Control.Category) where

import Prelude hiding (id, (.), foldr)
import Data.Foldable (Foldable, foldr) -- You might want to import everything.
import Control.Category (Category((.), id))
-- Try out replacing with:
-- import Control.Category ()
-- to see that no (.) is available.

Использование:

module Foo where
import Prelude()
import Prelude2

Чтобы узнать о типах foldr и (.) в Foo, как показано GHCi:

$ ghci Foo.hs
GHCi, version 7.6.3: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
[1 of 2] Compiling Prelude2         ( src-lib/Prelude2.hs, interpreted )
[2 of 2] Compiling Foo              ( /Users/pgiarrusso/AeroFS/Repos/pts-bluevelvet/src-lib/Foo.hs, interpreted )
Ok, modules loaded: Foo, Prelude2.
*Foo> :t foldr
foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
*Foo> :t (.)
(.) :: Category cat => cat b c -> cat a b -> cat a c
*Foo>

Если вы попытаетесь заменить

 import Control.Category ()

GHCi покажет вам, что (.) вообще не определен в Foo:

*Foo> :r
[1 of 2] Compiling Prelude2         ( src-lib/Prelude2.hs, interpreted )
[2 of 2] Compiling Foo              ( /Users/pgiarrusso/AeroFS/Repos/pts-bluevelvet/src-lib/Foo.hs, interpreted ) [Prelude2 changed]
Ok, modules loaded: Foo, Prelude2.
*Foo> :t (.)

<interactive>:1:1: Not in scope: `.'

Ответ 2

Почему это работает в ghci, но не в реальном?

Поскольку на вашем сеансе GHCi вы вводили выражения в контексте модуля MyList, поэтому F.foldr был в области видимости, но если вы импортируете MyList в другой модуль, тогда только имена, экспортированные MyList, и другие модули, которые вы импортировали, находятся в области.

Ваша догадка правильная - в каждом модуле, использующем Data.Foldable.foldr, вы должны

import qualified Data.Foldable as F

Имена, экспортируемые модулем, являются неквалифицированными; квалификация или нет этих имен определяется при импорте модуля.

На протяжении многих лет были предложения, позволяющие экспортировать квалифицированные имена, но на сегодняшний день ничего не было реализовано.

Ответ 3

Что касается неявного импорта Prelude, вы можете добавить следующую языковую прагму, а затем явно импортировать вещи из Prelude, но она может стать более уродливой, чем просто скрывать вещи из Prelude или использовать квалифицированный импорт Data.Foldable.

{-# LANGUAGE NoImplicitPrelude #-}

import Prelude (someFunction)

Почему это может стать более уродливым? Поскольку вам может потребоваться импортировать типы данных и функции, которые считаются само собой разумеющимися, или даже функции, которые явно не используются в коде:

{-# LANGUAGE NoImplicitPrelude #-}
module Main (main) where

-- we import fromInteger although it not explicitly used
import Prelude (Int, foldl, fromInteger, (+), putStrLn, (.), ($), IO, show)

sum :: [Int] -> Int
sum = foldl (+) 0

main :: IO ()
main = putStrLn . show $ sum [1,2,3]

Я говорил вам об этом не потому, что это хорошее решение, а просто для того, чтобы знать, что там такое.

Ответ 4

Вы можете создать модуль Prelude', который будет экспортировать только то, что вам нужно. Затем вы можете начать свой фактический модуль следующим образом:

import Prelude ()
import Prelude'
import Data.Foldable

Предположим, что вам нужно выполнить работу с ворчанием в Prelude', но по крайней мере это можно использовать повторно.

Разъяснение:

Prelude'.hs:

module Prelude' (
    id
  , Num (..)
  , everthing you want exported for your 'customized' prelude module goes in this list
  ) where

Ответ 5

Я перечитал ваш вопрос и некоторые ваши комментарии, и я думаю, что ближе всего вы можете добраться до того, что вы хотите, это примерно так:

module MyList
  (
    module Data.Foldable,
    MyList(..)
  )
where


import Data.Foldable
import Prelude (($))


data MyList a = MyList [a]

instance Foldable (MyList) where
    foldr f b (MyList as) = foldr f b as

В принципе, MyList реэкспортирует модуль Data.Foldable, так что кому-то, использующему ваш модуль, не придется импортировать Data. Снова снова, но... ей придется скрыть некоторые функции из Prelude.

module Main (main) where


import Prelude hiding (foldr)
import MyList


mySum :: MyList Int -> Int
mySum = foldr (+) 0

main :: IO ()
main = putStrLn . show . mySum $ MyList [1,2,3]

ИМХО, это хорошо. Вы не должны решать, как и что кто-то импортирует в свои собственные модули.