Правильный способ импортировать вещи, уже определенные в Prelude в Haskell - PullRequest
15 голосов
/ 04 июля 2010

Я пытаюсь определить 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 не найден, но удивительно, что F.foldr работает в GHCI.1029 *

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

Почему это происходитработать в ghci, а не в реале?

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

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

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


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

Какова обычная практика для решения такого рода проблем?

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

Ответы [ 5 ]

7 голосов
/ 04 июля 2010

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

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

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

import qualified Data.Foldable as F

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

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

6 голосов
/ 05 июля 2010

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

{-# LANGUAGE NoImplicitPrelude #-}

import Prelude (someFunction)

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

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

-- we import fromInteger although it's 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]

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

2 голосов
/ 10 августа 2014

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

Вы можете затем реэкспортировать также 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 голосов
/ 05 июля 2010

Я перечитал ваш вопрос и некоторые ваши комментарии, и я думаю, что самое близкое, что вы можете получить к тому, что вы хотите, это что-то вроде этого:повторно экспортирует модуль Data.Foldable, так что кому-то, использующему ваш модуль, не придется снова импортировать Data.Foldable, но ... ему придется скрыть некоторые функции из 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]

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

2 голосов
/ 04 июля 2010

Вы можете создать модуль 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...