Это от The Little MLer . У меня есть это

data Chain = Link Int (Int -> Chain)

И это

ints :: Int -> Chain
ints n = Link (n+1) ints

Я не совсем понимаю, что именно здесь происходит. Это похоже на бесконечную рекурсию самого себя, где ints в левой части просто бесконечно повторяет весь ints. В книге говорится

[...] мы можем думать о ints как о очень длинной последовательности ints. Мы идем с одного элемент в этой последовательности к следующему, применяя второй компонент из ints n к первому.

Я не знаю, как это происходит. Но тогда возникает ошибка

> ints 0
* No instance for (Show Chain) arising from a use of `print'
:     * In a stmt of an interactive GHCi command: print it

Попытка лавировать на deriving Show не сработает

data Chain = Link Int (Int -> Chain) deriving Show
No instance for (Show (Int -> Chain))
        arising from the second field of `Link' (type `Int -> Chain')
        (maybe you haven't applied a function to enough arguments?)
      Possible fix:
        use a standalone 'deriving instance' declaration,
          so you can specify the instance context yourself
    * When deriving the instance for (Show Chain)

Не уверен, что происходит и как действовать. Любые подобные примеры приветствуются.


Обновить

Вот код SML

datatype chain = Link of (int * (int -> chain))
fun ints (n) = Link (n+1, ints)
> ints(0)
val it = Link (1,fn) : chain

Не совсем уверен, но что fn - это способ анонимности в SML, т.е. fn - это их \. Это могло быть просто совпадением.

Итак, что есть в SML, с которым не может справиться Haskell? Связано ли это с тем, что Haskell чист, а SML - нет?

4
147pm 22 Мар 2021 в 08:27

3 ответа

Лучший ответ

Что происходит:

current :: Chain -> Int
current (Link i _) = i

next :: Chain -> Chain
next (Link i g) = g i

Как говорится в цитате. Ваш ints - это функция, поэтому здесь не происходит ничего бесконечного, просто отложенная возможность приложения функции получить следующий Link в цепочке, поэтому следующий {{X2 }} можно найти как элемент его current.

Что касается печати,

printLink :: Link -> String
printLink (Link i _) = "{Link of " ++ show i ++ " }"

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

Пример взаимодействия:

ch1 = ints 0
i1 = current ch1  -- 1
ch2 = next ch1  
i2 = current ch2  -- 2

И т.п.

Затем мы можем определить takeN :: Int -> Chain -> [Int] так, чтобы takeN 5 ch1 возвращал список из пяти Int из цепочки ch1, то есть [1,2,3,4,5].

SML строг, в нем не может быть бесконечных списков, как в Haskell. Поэтому вместо этого SML представляет собой «следующее» вычисление функцией, которую вы должны вызывать явно.

Поскольку Haskell ленив, мы также просто определяем chainToList :: Chain -> [Int] для преобразования цепочки в бесконечный список записей в ней, а затем используем для этого стандартную функцию take.

В вашем новом примере SML "fn" - это просто индикатор того, что там есть функция , какая-то функция. В вашем случае это функция ints, но система времени выполнения, очевидно, этого не знает. Это, вероятно, означает, что любая функция будет напечатана как "fn" в SML, как показывает ваш пример.

Но вам не нужно его распечатывать, чтобы использовать.

2
Will Ness 23 Мар 2021 в 06:52

Это случай того, что вы не прочитали достаточно далеко в тексте, а также получили ложное срабатывание с одного языка (SML), но не с другого. Как оказалось, использование функции, возвращающей Chain, такой как ints для прямого вывода, является ошибкой. Эти Chain "генераторы" существуют только как посредник для другой функции, которая "ищет", что где в таком объекте Chain, в примерах TLMLer, следующее целое число во второй части данных. конструктор.

data Chain = Link Int (Int -> Chain)

Итак, рассмотрим другой Chain генератор fiveOrsevenInts

mod5or7 n = if (evenDiv n 5) then True else (evenDiv n 7)
                where evenDiv a b = ((a `mod` b) == 0)

fiveOrsevenInts n = if (mod5or7 (n+1))
             then (Link (n+1) fiveOrsevenInts)
             else fiveOrsevenInts (n+1)

fiveOrsevenInts создает Chain на основе чисел, делящихся на 5 или 7. Как я узнал, вся идея заключается в том, чтобы «прочитать вперед» «цепочки». Так что если

fiveOrsevenInts (1) => Link 5 fiveOrsevenInts

Уловка состоит в том, чтобы заставить fiveOrsevenInts (функцию Int -> Chain) воздействовать на 5, чтобы получить следующий Int в цепочке. И, как я ошибочно подумал, нам вообще не нужно "видеть" указанное выше прямое разрешение fiveOrsevenInts (1).

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

chainItem n (Link i f) = if (n == 1)
                         then i
                         else chainItem (n-1) (f i)

Теперь, если я хочу увидеть первое в последовательности целых чисел, кратных 5 или 7, это просто

chainItem 1 (fiveOrsevenInts 0)
5
chainItem 2  (fiveOrsevenInts 0)
7
chainItem 3  (fiveOrsevenInts 0)
10
...
chainItem 37  (fiveOrsevenInts 0)
119

Где fiveOrsevenInts 0 "семена" Chain. Далее в этой главе эта стратегия применяется к нахождению последовательности простых чисел путем построения генератора простых чисел Chain. В общем, я нахожу The Little MLer интересным способом изучить его собрата Haskell. Когда я закончу, я постараюсь поделиться своим общим переводом.

0
147pm 23 Мар 2021 в 16:53

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

Вы можете написать его сами:

instance Show Chain where
  show (Link n fn) = ...

Но теперь вам нужно выяснить, как показать fn:: Int->Chain. По крайней мере, в Haskell функции атомарны и непрозрачны. Вы не можете разбить их или проверить их содержимое, только примените их.

6
John F. Miller 22 Мар 2021 в 07:05