在Monad.Reader第19期中的“协程管道”文章中,作者定义了一个泛型Coroutine类型:
newtype Coroutine f m a = Coroutine
{ resume :: m (Either (f (Coroutine f m a)) a)
}
Run Code Online (Sandbox Code Playgroud)
data FreeF f a b = Pure a | Free (f b)
newtype FreeT f m a = FreeT
{ runFreeT :: m (FreeF f a (FreeT f m a))
}
Run Code Online (Sandbox Code Playgroud)
看来FreeT和Coroutine是同构的。这是从一个映射到另一个的函数:
freeTToCoroutine
:: forall f m a. (Functor f, Functor m) => FreeT f m a …Run Code Online (Sandbox Code Playgroud) 以下代码改编自论文(RO Bjarnason,Stackless Scala With Free Monads).
本文的标题总体上指出了所提出的数据结构的目的 - 即在常量堆栈空间中提供递归处理,并让用户以清晰的方式表达递归.
具体来说,我的目标是建立一个monadic结构,在升序时基于恒定堆栈空间中的简单模式匹配,提供不可变树对(二叉树)或列表(n-ary-tree)的结构重写.
sealed trait Free[S[+_], +A]{
private case class FlatMap[S[+_], A, +B](
a: Free[S, A],
f: A => Free[S, B]
) extends Free[S, B]
def map[B](f: A => B): Free[S, B] = this.flatMap((a:A) => Done[S, B](f(a)))
def flatMap[B](f: A => Free[S, B]): Free[S, B] = this match {
case FlatMap(a, g) => FlatMap(a, (x: Any) => g(x).flatMap(f))
case x => FlatMap(x, f)
}
@tailrec
final def resume(implicit S: Functor[S]): Either[S[Free[S, A]], A] …Run Code Online (Sandbox Code Playgroud) 我有两个免费的monad用于不同的上下文中的不同操作.但是,如果特定操作在上下文中,则one(major)DSL需要包含另一个(action):
import Control.Monad.Free
data ActionFunctor next = Wait Timeout next
| Read URI next
instance Functor ActionFunctor where
fmap f (Wait timeout next) = Wait timeout (f next)
fmap f (Read uri next) = Read uri (f next)
type Action = Free ActionFunctor
data MajorFunctor next = Log LogText next
| Act Action next
| Send Message
instance Functor MajorFunctor where
fmap f (Log text next) = Log text (f next)
fmap f (Act action next) …Run Code Online (Sandbox Code Playgroud) ADT是免费的monad:
data Free f r = Free (f (Free f r)) | Pure r
Run Code Online (Sandbox Code Playgroud)
我希望它能够得到它,Show以便我可以在使用它时将其打印出来.例如,如果我有以下内容:
data T next = A next | B next deriving (Show)
aa = Free $ A $ Free $ B $ Pure ()
Run Code Online (Sandbox Code Playgroud)
就像现在一样,如果我添加deriving (Show)到FreeADT ,我会收到以下错误:
No instance for (Show (f (Free f r)))
arising from the first field of ‘Free’ (type ‘f (Free f r)’)
Possible fix:
use a standalone 'deriving instance' declaration,
so you can specify the …Run Code Online (Sandbox Code Playgroud) 我用sum数据类型创建了一个非常有用的Free Monad.这抽象了对持久性数据存储的访问:
data DataStoreF next =
Create Asset ( String -> next)
| Read String ( Asset -> next)
| Update Asset ( Bool -> next)
| UpdateAll [Asset] ( Bool -> next)
| Delete Asset ( Bool -> next)
| [...] -- etc. etc.
| Error String
type DataStore = Free DataStoreF
Run Code Online (Sandbox Code Playgroud)
我想将错误消息DataStore的实例MonadError处理为(Free (Error str)):
instance MonadError String DataStore where
throwError str = errorDS str
catchError (Free (ErrorDS str)) f = f str …Run Code Online (Sandbox Code Playgroud) 我有一些"遗留的"Scala代码(类似Java),它可以进行一些数据访问.有一个装饰器跟踪DAO方法的使用(收集指标),如下所示:
class TrackingDao(tracker: Tracker) extends Dao {
def fetchById(id: UUID, source: String): Option[String] = {
tracker.track("fetchById", source) {
actualFetchLogic(...)
}
}
...
}
Run Code Online (Sandbox Code Playgroud)
我试图把它塑造成一个免费的monad.我为DAO操作定义了以下代数:
sealed trait DBOp[A]
case class FetchById(id: UUID) extends DBOp[Option[String]]
...
Run Code Online (Sandbox Code Playgroud)
我看到两个选择:
a)我可以制作两个解释器DBOp,一个执行实际数据访问,另一个执行跟踪,并将它们组合在一起或者
b)我使跟踪成为一个显式代数,并使用Coproduct在同一个for组合中使用它们或者
c)完全不同的东西!
第一个选项看起来更像是一个"装饰器"方法,它与DBOp第二个选项相关,第二个选项是更通用的解决方案,但需要明确地调用"跟踪"代数.
另外,请注意source原始fetchById呼叫上的参数:它仅用于跟踪.我更愿意从API中删除它.
这是实际问题:如何建模跟踪?
我正在尝试使用免费的monad模式构建用于消息处理的管道,我的代码看起来像这样:
module PipeMonad =
type PipeInstruction<'msgIn, 'msgOut, 'a> =
| HandleAsync of 'msgIn * (Async<'msgOut> -> 'a)
| SendOutAsync of 'msgOut * (Async -> 'a)
let private mapInstruction f = function
| HandleAsync (x, next) -> HandleAsync (x, next >> f)
| SendOutAsync (x, next) -> SendOutAsync (x, next >> f)
type PipeProgram<'msgIn, 'msgOut, 'a> =
| Act of PipeInstruction<'msgIn, 'msgOut, PipeProgram<'msgIn, 'msgOut, 'a>>
| Stop of 'a
let rec bind f = function
| Act x -> x |> …Run Code Online (Sandbox Code Playgroud) f# asynchronous computation-expression async-await free-monad
在purescript-free程序包中,有一个定义此解释器的代码示例:
teletypeN :: forall eff. NaturalTransformation TeletypeF (Eff (console :: CONSOLE | eff))
teletypeN (PutStrLn s a) = const a <$> log s
teletypeN (GetLine k) = pure (k "fake input")
Run Code Online (Sandbox Code Playgroud)
如何定义和运行返回类型为Array Int或的其他解释器State String Int?
动机:能够在Free/ Freer-style中控制MTL中的效果.
这个例子可能有点做作 - 想象一个带有一些基本操作的程序(使用GHC 8.2 freer-simple),
#!/usr/bin/env stack
-- stack --resolver lts-10.2 --install-ghc runghc --package freer-simple
{-# LANGUAGE GADTs #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE LambdaCase #-}
import Control.Monad.Freer
data Effect next where
ReadFilename :: String -> Effect String
WriteOutput :: Show a => a -> Effect ()
Computation :: Int -> Int -> Effect Int
readFilename :: Member Effect effs => String -> Eff effs String
readFilename = send …Run Code Online (Sandbox Code Playgroud) 我对函数式编程很陌生。但是,我阅读了有关 Free Monad 的文章,并且正在尝试在玩具项目中使用它。在这个项目中,我对股票的投资组合域进行建模。正如许多书中所建议的那样,我为 定义了 PortfolioService一个代数,为PortfolioRepository.
我想在PortfolioRepository代数和解释器的定义中使用 Free monad 。目前,我没有PortfolioService根据 Free monad 来定义代数。
但是,如果我这样做,在PortfolioService解释器中,PortfolioRepository由于使用的 monad 不同,我无法使用 the 的代数。例如,我不能使用 monads Either[List[String], Portfolio],并且Free[PortfolioRepoF, Portfolio]在同一个for-comprehension 中:(
我怀疑如果我开始使用 Free monad 来模拟代数,那么所有其他需要与它组合的代数都必须根据 Free monad 来定义。
这是真的吗?
我正在使用 Scala 和 Cats 2.2.0。
free-monad ×10
haskell ×5
scala ×3
monads ×2
scala-cats ×2
async-await ×1
asynchronous ×1
conduit ×1
coroutine ×1
f# ×1
printing ×1
purescript ×1
show ×1
stackless ×1
typeclass ×1