¿Es posible escribir software con una máquina de estado usando la programación funcional? Como la programación funcional no tiene estado, la respuesta debería ser intuitivamente imposible.

La programación funcional no es apátrida; más bien, la programación funcional le proporciona formas de controlar el estado.

Además, el “estado” en la “máquina de estados” está completamente divorciado del estado a nivel de lenguaje. ¡Las máquinas de estado a menudo se definen puramente en términos de funciones matemáticas sin ninguna referencia al estado mutable en absoluto! No solo podemos representar una máquina de estado en un lenguaje funcional, sino que la representación en realidad se parece más a cómo hablaríamos de máquinas de estado a un alto nivel.

Por ejemplo, un autómata determinista de estado finito (DFA) generalmente se define en términos de un par de conjuntos (los estados [matemáticas] Q [/ matemáticas] y el alfabeto [matemáticas] \ Sigma [/ matemáticas]), el comienzo [matemáticas ] s [/ math] y acepta estados y una función de transición. Podemos representar esto con bastante fidelidad en Haskell, utilizando tipos en lugar de los conjuntos:

datos DFA qs = DFA {
inicio :: q,
aceptar :: [q],
paso :: q -> s -> q
}

El DFA se ejecuta aplicándolo a una cadena de elementos desde s (es decir, [s] ) y verificando si el estado resultante es uno de los estados aceptados. De hecho, esto es solo un pliegue:

– | ¿El DFA dado acepta la cadena dada?
ejecutar DFA {start, accept, step} string = foldl step start string `elem` accept

Entonces podríamos usar esta definición para DFA específicos especificando los tipos correctos para los estados y el alfabeto y proporcionando los tres campos. El artículo de Wikipedia tiene una máquina de ejemplo que combina múltiplos de 3 en binario:

El alfabeto es cadenas de dos símbolos y tenemos tres estados:

datos Σ = A | B derivando la ecuación

datos Q = S₀ | S₁ | S₂ derivando la ecuación

(Me gusta usar Unicode pero, por supuesto, no es necesario).

La función de paso es natural para escribir con coincidencia de patrones:

paso ‘S₀ A = S₀
paso ‘S₀ B = S₁
paso ‘S₁ A = S₂
paso ‘S₁ B = S₀
paso ‘S₂ A = S₁
paso ‘S₂ B = S₂

Finalmente, podemos poner todo junto:

wikiDFA = DFA {
inicio = S₀,
aceptar = [S₀],
paso = paso ‘
}

Ahora podemos usar esto para verificar cadenas en nuestro formato:

* Principal> ejecutar wikiDFA [B, A, B, A, B, A]
Cierto
* Principal> ejecutar wikiDFA [B, A, B, A, B, B]
Falso

¡Así que implementar claramente un DFA es poco más que transcribir la notación normal en una sintaxis ligeramente diferente!

Por supuesto, en la práctica, hay muchas razones diferentes para implementar una máquina de estado, por lo que no siempre querrá usar este método en particular, ya sea por razones de legibilidad o rendimiento. Y los lenguajes funcionales admiten todas estas otras formas, incluido el uso del estado mutable real. Lo importante de mi respuesta no son los detalles específicos de lo que hice, sino el hecho de que no solo es posible sino también simple y natural .

Es posible que desee comprobar CLaSH (desde Haskell hasta hardware), es un lenguaje de descripción de hardware que utiliza GHC como interfaz. Lo he usado para un proyecto de posgrado y realmente me gusta.

El lenguaje básicamente es un Haskell sin recursión (o con recursión limitada).

Cuando escribe lógica secuencial para circuito digital, tiene que escribir la máquina de estado explícitamente, y encuentro que la gramática de coincidencia de patrones de Haskell es muy adecuada para escribir la máquina de estado.

las dos funciones básicas para escribir la máquina de estados son “harinosa” y “moore”, podrían sintetizar su función de transformación de estado en un circuito digital secuencial. y la firma tipo para “mealy” es:

harinoso :: (s -> i -> (s, o)) -> s -> Señal i -> Señal o

la firma de tipo es su documento, le das una función que toma un estado antiguo y una señal de entrada de circuito como entrada de función, y la salida es una tupla de estado nuevo y salida de circuito, y luego das un estado inicial, luego obtienes Un circuito modelo harinoso.

La programación funcional no es, por definición, sin estado. Puede modelar problemas con estado utilizando la programación funcional igualmente bien. Lo que aboga por la programación funcional es una clara separación entre las funciones / programas libres de efectos secundarios y las funciones / programas completos de efectos secundarios . Esta distinción puede ayudar en múltiples frentes, como razonar sobre la funcionalidad o lograr ejecuciones concurrentes eficientes. Al entrar en más detalles, la programación funcional proporciona un conjunto de facilidades, que pueden ayudar a aclarar esta distinción (por ejemplo, la mónada IO es un caso así).

Para volver a su pregunta, un programa puramente funcional es aquel que siempre devuelve el mismo resultado para la misma entrada (también conocido como cumple con la definición matemática de funciones). Esto parece entrar en conflicto con las máquinas de estado, considerando que para la misma entrada pueden devolver diferentes salidas, dependiendo del estado actual. Aquí viene el giro de la trama: ¿qué pasa si modela el estado como parte de la función?

Por ejemplo, en lugar de definir la siguiente función:

Salida getMachineOutput (Entrada de entrada) {

}

usted define el siguiente:

Tupla getMachineOutput (Entrada, Estado s) {

}

Puede decir fácilmente que acabo de barrer el “estado” debajo de la alfombra, ya que tiene que haber alguien que procese la salida de esta función y, por lo tanto, tenga que almacenar el estado. Y tendrá razón, la mayoría de las aplicaciones prácticas producen su valor mediante el uso de algún estado. Y la programación funcional no afirma que haya una manera de deshacerse de este estado por completo. Sin embargo, sugiere una forma de mantener la mayor parte de nuestras bases de código lo más libres de efectos secundarios posible y llevar todos los efectos secundarios al “borde” de nuestras aplicaciones, con todos los beneficios que brinda este enfoque.

Las máquinas de estados se definen matemáticamente como un conjunto de estados y una función de transición. Y, como probablemente ya sabe, las definiciones matemáticas son completamente apátridas. Puede emular esta apatridia en cualquier lenguaje funcional. La belleza de los lenguajes funcionales es que te permiten ser tan fiel a las matemáticas como sea razonablemente posible. Es una de las mayores alegrías de la programación funcional.

Definitivamente es posible, y de hecho, lo hice hace aproximadamente un año. Escribimos un compilador usando Haskell y manejamos el estado de manera eficiente. También señalaré que el curso usó anteriormente C pero con Haskell, podemos escribir una fracción del código, lo que lo hace mucho más fácil de leer y comprender.

No diría que los lenguajes funcionales no tienen estado, pero es mejor visualizarlo como un diagrama de flujo recursivo si lo desea. Con Haskell, casi todo es recursivo, por lo que los estados en nuestro compilador se manejan no solo como parámetros, sino también como definiciones de funciones en las que la salida depende de qué estado se haya dado. Piense en una declaración recursiva de cambio / caso que modele el flujo de estado. Fue muy difícil, pero finalmente Haskell fue una elección audaz e inteligente para el estado y los compiladores en general. ¡Me encantaría compartir más detalles sobre el proyecto!

Sí lo es. En realidad, mi respuesta es una versión abreviada de las respuestas ya dadas:
Considere su estado actual como S1. Aplique la función f y obtendrá el estado S2, que reemplaza a S1.

No hay razón por la que no puedas. La programación funcional puede ser con estado (todos los programas útiles tienen estado), solo desalienta el estado mutable.

En lugar de cambiar el valor de una variable en su lugar, crea una nueva instancia de una variable y la usa para almacenar el nuevo valor.
Autómata finito en Haskell
http://nuerd.blogspot.nl/2013/06

De hecho, encuentro que la programación funcional hace que sea más fácil escribir la máquina de estados, debido a la coincidencia de patrones.
Por ejemplo, puedo hacer lo siguiente

f (0: xs) = g xs
f (1: xs) = h xs
f (x: xs) = f xs

en haskell, donde f, g, h representan todos los estados.
Esto es bastante elegante, en comparación con su versión equivalente en programación imperativa.