State Machine Pattern в Haskell: ошибка бесконечного типа

Я пытался реализовать конечный автомат в Haskell. Упрощенная версия:

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

Здесь мой подход: просто каждое состояние будет функцией, которая возвращает как свой вывод, так и функцию, соответствующую другому состоянию.

module Main where

a x | x == 0 = (0,b)
a x = (2*x, a)

b x | x == 0 = (0,a)
b x = (x, b)

evalstate s [] = []
evalstate s (x:xs) = (v:evalstate s' xs)
    where (v,s') = s x

main :: IO ()
main = putStrLn $ show $ evalstate a [1,1,2,3,4,0,2,3,3]

К сожалению, типы для a и b бесконечны, и GHC жалуется:

Occurs check: cannot construct the infinite type: t = t1 -> (t2, t)

Каков способ выразить этот шаблон в Haskell?

Я мог бы, конечно, сделать что-то вроде:

s 'a' x | x == 0 = (0,'b')

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

Ответ 1

Вы пытаетесь определить конечный автомат с типом

type SM = Int -> (Int, SM)

но Haskell этого не допускает. Вы должны использовать data или newtype, чтобы ввести новый именованный тип:

newtype SM = SM (Int -> (Int, SM))

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

module Main where

newtype SM = SM (Int -> (Int, SM))

a = SM a'
    where
    a' x | x == 0 = (0, b)
    a' x = (2 * x, a)

b = SM b'
    where
    b' x | x == 0 = (0, a)
    b' x = (x, b)

evalstate s [] = []
evalstate (SM s) (x : xs) = (v:evalstate s' xs)
    where (v, s') = s x

main :: IO ()
main = putStrLn $ show $ evalstate a [1,1,2,3,4,0,2,3,3]