Правильный способ импорта вещей, уже определенных в 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]
ИМХО, это хорошо. Вы не должны решать, как и что кто-то импортирует в свои собственные модули.