Tengo un estado general que es esencialmente un 3-tuple, y una serie de funciones que se refieren a partes de ese estado. Estoy tratando de encontrar un conjunto de adaptadores genéricos para tales funciones, de modo que pueda usarlos en una canalización de mónadas de estado.Mónada de estado: ¿funciones de adaptación que solo funcionan con partes del estado?
Esto es posiblemente totalmente erróneo; siéntete libre de hacer ese caso.
Pido disculpas de antemano por la mezcla de Java y pidgin Scala. De hecho, estoy haciendo esto en Java como un ejercicio de aprendizaje, pero nadie tiene tiempo para leer todo eso. He eludido una gran cantidad de complejidad desinteresada por el bien de la discusión; no te preocupes por el modelado del dominio.
El estado en cuestión es la siguiente:
ImportState(row:CsvRow, contact:Contact, result:ImportResult)
ImportResult
es uno de ADD
, MERGE
o REJECT
.
Las funciones que hemos definido son los siguientes:
def rowToContact: ImportRow => Contact
def findMergeCandidates: Contact => (Contact, List[Contact])
// merges, or declines to merge, setting the result
def merge: (Contact, List[Contact]) => (Contact, ImportResult)
def persist: Contact => ImportResult
def commitOrRollback: ImportState => ImportState
def notifyListener: ImportState => Nothing
Los adaptadores he definido hasta ahora son bastante simples, y se ocupan de las propiedades individuales de ImportState
:
def getRow: ImportState => ImportRow
def getContact: ImportState => Contact
def setRow(f: _ => ImportRow): ImportState => ImportState
def setContact(f: _ => Contact): ImportState => ImportState
def setResult(f: _ => ImportResult): ImportState => ImportState
El (roto) de tuberías es como la siguiente (en Java):
State.<ImportState>init()
.map(setRow(constant(row)))
.map(setContact(getRow.andThen(rowToContact)))
.map(getContact.andThen(findMergeCandidates).andThen(merge)) // this is where it falls apart
.map(setResult(getContact.andThen(persist)))
// ... lots of further processing of the persisted contact
.map(commitOrRollback)
.map(notifyListener);
El immediat El problema es que merge
devuelve una tupla (Contact, ImportResult)
, que me gustaría aplicar a dos propiedades del estado (contact
y result
), manteniendo la tercera propiedad, row
.
Hasta ahora, yo he llegado con un par de enfoques para la adaptación de fusión que tanto chupar:
definir algunas funciones que empacar y desempacar tuplas, y utilizarlos directamente en la tubería. Esta opción es extremadamente ruidosa.
Defina un adaptador único para
ImportState
ymerge
. Esta opción se siente como darse por vencido.
¿Hay una manera mejor?
Gracias! Lo etiqueté Haskell porque puedo leer más o menos Haskell, con la ayuda de un diccionario Haskell-Inglés. – loganj
Esto parece que une mis funciones get/set. Para un estado con n campos, ¿existe un modismo Haskell para componer funciones de extracción y actualización para que no necesite n^2-1 de cada una? – loganj
@loganj: sí, en el paquete fclabels, por ejemplo, el tipo de lente es una instancia de "Data.Category.Category", lo que significa que define un "lente de identidad" 'id :: Lens aa' y una" composición de lente " '(.) :: Lente bc -> Lente ab -> Lente ac'. La definición es bastante simple: el nuevo get es solo la composición de los antiguos, y con una función auxiliar 'modify :: Lens ab -> (b -> b) -> a -> a'., The new 'set 'es algo así como: 'modificar lens1 outer (\ inner -> update lens2 inner x)'. Esto es un poco feo debido a mi elección de orden de argumento en 'update', pero espero que tenga sentido de todos modos. – mokus