州monad - 适应仅与州的部分地区合作的功能?

log*_*anj 5 monads state haskell functional-programming scala

我有一个基本上是3元组的一般状态,以及许多函数,每个函数都涉及该状态的一部分.我正在尝试为这些函数设计一组通用适配器,以便我可以在状态monad管道中使用它们.

这可能完全是错误的; 随意做出这种情况.

我提前为Java和pidgin Scala的混合道歉.我实际上是用Java作为学习练习,但是没有人有时间阅读这些内容.为了讨论,我已经省略了许多无趣的复杂性; 不要担心域建模.

有问题的国家是这样的:

ImportState(row:CsvRow, contact:Contact, result:ImportResult)
Run Code Online (Sandbox Code Playgroud)

ImportResult是一个ADD,MERGEREJECT.

我定义的功能是这些:

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
Run Code Online (Sandbox Code Playgroud)

到目前为止我定义的适配器非常简单,并处理以下各项属性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
Run Code Online (Sandbox Code Playgroud)

(破碎的)管道看起来像这样(在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);
Run Code Online (Sandbox Code Playgroud)

直接的问题是merge返回一个元组(Contact, ImportResult),我想将其应用于状态(contactresult)的两个属性,同时保留第三个属性,row.

到目前为止,我已经提出了两种适应合并的方法,这两种方法都很糟糕:

  1. 定义一些打包和解包元组的函数,并直接在管道中使用它们.这个选项非常嘈杂.

  2. ImportState和定义一次性适配器merge.这个选项感觉就像放弃了.

有没有更好的办法?

mok*_*kus 7

你的问题被标记为Haskell - 我希望这意味着你可以阅读Haskell,而不是有人看到'monads'并添加它.根据这个假设,我将在这个答案中讲Haskell,因为它是我现在认为的语言;)

有一个称为"功能镜头"的有用概念,带有几个Haskell库实现.核心思想是"镜头"是一对功能:

data Lens a b = Lens { extract :: (a -> b), update :: (a -> b -> a) }
Run Code Online (Sandbox Code Playgroud)

这代表了获取和更新结构"部件"的功能性方法.使用这样的类型,您可以编写如下函数:

subState :: Lens a b -> State a t -> State b t
subState lens st = do
    outer <- get
    let (inner, result) = runState st (extract lens outer)
    put (update lens outer inner)
    return result
Run Code Online (Sandbox Code Playgroud)

将其翻译成Java听起来像一个有趣(可能非常具有挑战性)的练习!