Haskell: нет экземпляра (num ()) - определение моего собственного монадического цикла

Я пытаюсь выполнить это упражнение в вики Haskell: https://en.wikibooks.org/wiki/Haskell/Higher-order_functions

The following exercise combines what you have learned about higher order functions, recursion and I/O. We are going to recreate what is known in imperative languages as a for loop. Implement a function

for :: a -> (a -> Bool) -> (a -> a) -> (a -> IO ()) -> IO () 
for i p f job = -- ???

пока у меня есть:

-- for : init value, end condition function, increment function, IO function, 
--       returns IO action

generate :: a -> (a->Bool) -> (a->a) -> [a]
generate s cnd incr = if (cnd s) then [] else [s] ++ generate (incr s) cnd incr

printToList  = do
               u <- print 1
               v <- print 2
               return [u,v]

ioToASingle :: [IO a] -> IO [a]
ioToASingle (x:xs) = do
                 x' <- x
                 return [x']

sequenceIO :: [IO a] -> IO [a]
sequenceIO [] = return []
sequenceIO (x:xs) = do
                     x' <- x
                     xs' <- sequenceIO xs
                     return ([x'] ++ xs')

for::a->(a->Bool)->(a->a)->(a->IO())->IO()
for s cnd incr ioFn = sequence_ (map (ioFn) (generate s cnd incr))

for'::a->(a->Bool)->(a->a)->(a->IO a)->IO [a]
for' s cnd incr ioFn = sequenceIO (map (ioFn) (generate s cnd incr))

for работает нормально:

for 1 (\i->i==10) (\i->i+1) (print)
1
2
3
4
5
6
7
8
9

for' выдает ошибку:

*Main> for' 1 (\i->i==10) (\i->i+1) (print)

<interactive>:323:6: error:
    • No instance for (Num ()) arising from the literal ‘1’
    • In the first argument of ‘for'’, namely ‘1’
      In the expression: for' 1 (\ i -> i == 10) (\ i -> i + 1) (print)
      In an equation for ‘it’:
          it = for' 1 (\ i -> i == 10) (\ i -> i + 1) (print)
*Main>

Я не могу понять, что случилось.

Тип (print) - Show a => a -> IO (), а не Show a => a -> IO a. Вам нужно объединить его с чем-то еще, чтобы вернуть ввод обратно.

Thilo 26.10.2018 08:21
2
1
90
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Тип возврата sequenceIO - это IO [a], но ioFn (как в определении for ') - это «print», который возвращает IO (). Вам нужен ioFn, чтобы вернуть IO a, так что возвращение sequenceIO будет IO [a].

например попробуйте «return» вместо «print», а затем распечатайте значение. return - это функция в haskell, которая просто преобразует переданный аргумент в монаду, в данном случае монаду ввода-вывода.

main = do p <- for' 1 (\i->i==10) (\i->i+1) (return) 
         print(p) 

Это просто напечатает список от 1 до 9 чисел.

Или

p :: Int -> IO [Int]
p x =  for' 1 (\i->i==x) (\i->i+1) (return)

main = do y <- p 10
         print (y)  

Вы имели в виду "ioFn, который возвращает IO a".

Will Ness 26.10.2018 12:00

Кроме того, ваш отступ неправильный. нужно либо исправить, либо добавить явные разделители { ; }, либо оба.

Will Ness 26.10.2018 12:07
Ответ принят как подходящий

Вот что вы написали:

    for' :: a -> (a -> Bool) -> (a -> a ) -> (a -> IO a ) -> IO [a]
    for'    s    cnd            incr         ioFn         =  sequenceIO (map (ioFn) ...

*Main> for' 1    (\i->i==10)    (\i->i+1)    (print     )      
            a                                (a -> IO ())
                                             ------------
            ()                                      a ~ ()

Таким образом, 1 :: (Num a) => a ~ (Num ()) => (). Но для типа Num нет экземпляра ().

Это обходной способ GHC сказать вам, что вам нужна функция типа a -> IO a, а не a -> IO ().

Хиндли-Милнер допускает унификацию с «более узким» типом, но здесь было бы лучше, если бы этого не было - сообщение об ошибке было бы намного яснее.

В любом случае легко придумать свой собственный ценить - возвращающий Распечатать, например

myprint :: a -> IO a
myprint x = do { print x      -- or,  print x >> return x
               ; return x 
               }

@luqui спасибо; пустое пространство - важный инструмент. В Лиспе тоже.

Will Ness 26.10.2018 17:57

Другие вопросы по теме