我一直在尝试简单的 Monad Transformers,其中我对涉及M[F[A]]whereM和Fare monad 的理解。如果是不同的单子,我如何在 for comp 中一起制作M[F[A]]和工作?M[S[A]]S
例如:
val a: Future[List[Int]] = ...
val b: Future[Option[Int]] = ...
Run Code Online (Sandbox Code Playgroud)
a需要 aListT[Future, Int]和b需要 anOptionT[Future, Int]但这些不组成,我需要使用另一个变压器吗?这取决于我在 for comp 中使用它们的顺序吗?
假设我有一个像这样的单子堆栈:
import Control.Monad.Trans.Reader
import Control.Monad.Trans.Except
import Control.Monad.Trans
type MyMonad = ReaderT Env (ExceptT String IO) -- Env is irrelevant
Run Code Online (Sandbox Code Playgroud)
和一个函数(简化,但想法成立):
f :: Integer -> MyMonad Integer
f 42 = lift $ throwE "42 is an ILLEGAL number"
f n = return n
Run Code Online (Sandbox Code Playgroud)
我现在想做的是f从另一个函数调用,但如果发生则捕获抛出的异常并以某种方式处理它(例如,抛出另一个异常但消息已更改)。我很难弄清楚应该在这里进行什么样的电梯操作才能正确完成。我尝试过这样的事情:
g n = do
x <- (f n) `catchE'` (\_ -> lift $ throwE "nope, still illegal")
return x
where catchE' - lift . catchE
Run Code Online (Sandbox Code Playgroud)
但它显然行不通,因为catchE'在 monad 中需要一些东西ExceptT,而不是MyMonad. 可以轻松完成吗?也许改变 …
我试图理解读者单子转换器。我正在使用FSharpPlus并尝试编译以下示例,该示例首先从阅读器环境中读取某些内容,然后执行一些异步计算,最后合并两个结果:
open FSharpPlus
open FSharpPlus.Data
let sampleReader = monad {
let! value = ask
return value * 2
}
let sampleWorkflow = monad {
do! Async.Sleep 5000
return 4
}
let doWork = monad {
let! envValue = sampleReader
let! workValue = liftAsync sampleWorkflow
return envValue + workValue
}
ReaderT.run doWork 3 |> Async.RunSynchronously |> printfn "Result: %d"
Run Code Online (Sandbox Code Playgroud)
有了这个,我在它显示的行出现编译错误,let! value = ask并显示以下完全无用的(至少对我来说)错误消息:
将默认类型“obj”应用于类型推断变量时,类型约束不匹配。没有与方法“op_GreaterGreaterEquals”匹配的重载。
已知返回类型:异步
已知类型参数:< obj , (int -> Async) >
感觉就像我只是在某个地方缺少一些操作员,但我无法弄清楚。
我希望获得一些有助于理解 Monad Transformer 的信息,以及与此相关的使用do符号会发生什么。我试图理解的例子如下:
data ProtectedData a = ProtectedData String a
accessData :: String -> ProtectedData a -> Maybe a
accessData s (ProtectedData pass v) =
if s == pass then Just v else Nothing
type Protected s a = MaybeT (Reader (ProtectedData s)) a
-- untangles the monad construction
run :: ProtectedData s -> Protected s a -> Maybe a
run ps psa = runReader (runMaybeT psa) ps
access :: String -> Protected a a
access pass …Run Code Online (Sandbox Code Playgroud) 我正在用 C++ 编写一些相对函数式风格的代码,并试图避免为其起一个坏名字。为此,我尽可能坚持 Haskell 中同构概念的名称。(需要明确的是,我不太了解 Haskell,但阅读它非常有启发性。)
如果我正确地完成了 Haskell 打字,我相信我正在寻找名称的概念将具有以下签名:
someName :: (Monad m1, Monad m2) => (a -> m1 b) -> (m2 a -> m1 m2 b)
Run Code Online (Sandbox Code Playgroud)
这个想法是,您有一个适合用于绑定类型 monad 的函数m1,并将其转换为适合绑定m1包含m2值的值的函数。
我不能很好地编写 Haskell 来给出一个例子,但这里是伪 C++ 说明我想要的,使用std::optional和absl::StatusOr:
// A silly example of a bind function for absl::StatusOr<int>.
absl::StatusOr<int> AddTwoUnless17(int x) {
if (x == 17) {
return absl::InternalError("can't add two to 17!")
}
return x + 2;
}
// Convert AddTwoUnless17 into …Run Code Online (Sandbox Code Playgroud) Haskell的Text.JSON库使用一个名为Result的抽象数据类型,它基本上是它们的Maybe形式,但是没有Nothing,就有错误字符串.Anywho,我需要使用liftIO转换一个函数调用,将一个IO事件返回到我的JSON.readJSON实现中的Result事物.我是monad变换器的新手,似乎无法为Result实现liftIO(我继续尝试构造无限类型,根据ghci).
有任何想法吗?
非常感谢
编辑
对不起,我花了很长时间来详细说明!我感谢你的帮助.
readJSON (JSObject obj) = do text <- getVal obj "text"
user <- getVal obj "from_user"
iden <- getVal obj "id_str"
url <- (do if (length.extractURLs) text == 0
then return ""
else return $ head $ extractURLs text)
title <- liftIO (getSiteTitle url)
return $
Tweet
NewsStory {
title = "Twitter",
desc = text,
url = url,
metric = 0,
sourceURL = "twitter.com/" ++ user ++ "/status/" ++ iden
}
Run Code Online (Sandbox Code Playgroud)
因此返回前的最后一行使用getSiteTitle来解析该URL的网站以获取其标题.但是,该函数返回一种IO字符串,编译器告诉我它希望它是Result.这不可能吗?
再次感谢!
EDIT2
我决定从我的数据类型中删除标题,然后在IO monad中获取它.谢谢大家的帮助!我当然从这个问题中吸取了教训.
这是一些示例代码
foo :: a -> Identity (Maybe a)
foo a = do
maybeStuff <- getStuffSometimes a
return $ case maybeStuff of -- this "case" stuff is the kind
Just stuff -> Just $ getStuffAlways stuff -- of code I'd expect the Maybe
Nothing -> Nothing -- monad to help with
getStuffSometimes :: a -> Identity (Maybe a)
getStuffSometimes a = return $ Just a
getStuffAlways :: a -> Identity a
getStuffAlways = return
-- ERROR (on the return statement of …Run Code Online (Sandbox Code Playgroud) 我正在使用此代码的第一个版本StateT L8.ByteString Maybe a.到目前为止,我已将大部分功能转化为此功能
matchHeader :: L8.ByteString -> StateT L8.ByteString Maybe ()
matchHeader prefix = StateT $ \str ->
if prefix `L8.isPrefixOf` str
then Just ((), L8.drop (L8.length prefix) str)
else Nothing
getNat :: Num a => StateT L8.ByteString Maybe a
getNat = StateT $ \str ->
case L8.readInt str of
Nothing -> Nothing
Just (num, rest)
| num <= 0 -> Nothing
| otherwise -> Just (fromIntegral num, rest)
getBytes :: Integer -> StateT L8.ByteString …Run Code Online (Sandbox Code Playgroud) 我有两个或更多独立状态要在一个Haskell应用程序中跟踪.
我正在使用声明两个新类型
type MonadTuple m = MonadState (Int, Int) m
type MonadBool m = MonadState Bool m
Run Code Online (Sandbox Code Playgroud)
monad变换器堆栈声明为
type Stack = StateT (Int, Int) (StateT Bool IO) ()
Run Code Online (Sandbox Code Playgroud)
我打算像这样使用堆栈
ret :: Stack
ret = apply
apply :: (MonadTuple m, MonadBool m) => m ()
apply = undefined
Run Code Online (Sandbox Code Playgroud)
该编译器是不高兴,因为它不能匹配Bool与(Int, Int)试图检查时Stack符合MonadBool.
我知道在StateT中组合多个状态时给出的解决方案.除了箭头或带镜头的全局状态之外,还有其他更简单的解决方案吗?
附录:完整的代码块是
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FlexibleContexts #-}
import Control.Monad.State.Class
import Control.Monad.State.Lazy
type MonadTuple m …Run Code Online (Sandbox Code Playgroud) 假设我有一些返回的函数Async<Result<string>>:
let getData id = async {
return Ok (string id)
}
Run Code Online (Sandbox Code Playgroud)
现在,此函数的输入是另一个返回函数的结果Result<int>.
我正在努力研究如何Result.bind在异步CE内部组合2 .
例如:
let main = async {
let id = Ok 123
let! x = id |> Result.bind getData
return x
}
Run Code Online (Sandbox Code Playgroud)
这不起作用,我收到错误:
error FS0001: Type mismatch. Expecting a
'Result<int,'a> -> Async<'b>'
but given a
'Result<int,'a> -> Result<'c,'a>'
Run Code Online (Sandbox Code Playgroud)
或者如果我不使用let!我得到并且只是使用let
error FS0001: Type mismatch. Expecting a
'int -> Result<'a,'b>'
but given a
'int -> Async<Result<string,'c>>
Run Code Online (Sandbox Code Playgroud)
我见过一些答案是说没有使用Result<'a>,只是让异步异常处理做了艰苦的工作,但我面对同样的问题 …