天道酬勤,学无止境

具有多种 monad 类型的 Haskell do 子句(Haskell do clause with multiple monad types)

问题

我在 Haskell 中使用了一个名为 Threepenny-GUI 的图形库。 在这个库中,main 函数返回一个 UI monad 对象。 这让我很头疼,因为当我尝试将IO值解包到局部变量中时,我收到抱怨不同 monad 类型的错误。

这是我的问题的一个例子。 这是标准 main 函数的略微修改版本,如 Threepenny-GUI 的代码示例所示:

main :: IO ()
main = startGUI defaultConfig setup

setup :: Window -> UI ()
setup w = do

labelsAndValues <- shuffle [1..10]

shuffle :: [Int] -> IO [Int]
shuffle [] = return []
shuffle xs = do randomPosition <- getStdRandom (randomR (0, length xs - 1))
                let (left, (a:right)) = splitAt randomPosition xs
                fmap (a:) (shuffle (left ++ right))

请注意第五行:

labelsAndValues <- shuffle [1..10]

返回以下错误:

Couldn't match type ‘IO’ with ‘UI’
Expected type: UI [Int]
  Actual type: IO [Int]
In a stmt of a 'do' block: labelsAndValues <- shuffle [1 .. 10]

至于我的问题,我如何使用标准箭头符号 ( <- ) 解压缩IO函数,并继续将这些变量作为IO ()而不是UI () ,以便我可以轻松地将它们传递给其他函数。

目前,我找到的唯一解决方案是使用liftIO,但这会导致转换为UI monad 类型,而我实际上想继续使用IO类型。

回答1

do块用于特定类型的 monad,您不能只更改中间的类型。

您可以转换动作,也可以将其嵌套在do 。 大多数情况下,转换将为您做好准备。 例如,您可以有一个与io一起使用的嵌套do ,然后仅在交互点转换它。

在您的情况下,ThreePennyUI 包提供了一个liftIOLater函数来为您处理此问题。

liftIOLater :: IO () -> UI ()

安排稍后运行的 IO 操作。

为了执行逆转换,您可以使用runUI

runUI :: Window -> UI a -> IO a

在特定浏览器窗口中执行 UI 操作。 还运行所有计划的 IO 操作。

回答2

这更像是一个扩展评论 - 它没有解决主要问题,而是您对shufffle的实现。 它有两个问题:

  1. 您的实施效率低下 - O(n^2)
  2. IO不是它的正确类型 - shuffle 没有一般的副作用,它只需要一个随机源。

对于(1)有几种解决方案:一种是使用Seq及其索引,即O(log n) ,这将使shuffle O(n log n) 。 或者您可以使用 ST 数组和标准算法之一来获得O(n)

对于 (2),您只需要线程化一个随机生成器,而不是IO全部功能。 已经有很好的库 MonadRandom 定义了一个用于随机计算的 monad(和一个类型类)。 另一个包已经提供了 shuffle 功能。 由于IOMonadRandom一个实例,您可以直接使用shuffle作为您的函数的替代品。

回答3

在幕后,do 只是 >>= (bind) 的语法糖,然后让:

do { x<-e; es } =   e >>= \x -> do { es }
do { e; es }    =   e >> do { es }
do { e }        =   e
do {let ds; es} =   let ds in do {es} 

和绑定的类型:

(>>=) :: Monad m => a -> (a -> m b) -> m b

所以是的,它只“支持”一个 Monad

受限制的 HTML

  • 允许的HTML标签:<a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • 自动断行和分段。
  • 网页和电子邮件地址自动转换为链接。

相关推荐
  • 什么是单子?(What is a monad?)
    问题 最近对Haskell进行了简要介绍之后,关于monad本质上是什么的简要而简洁的实际解释是什么? 我发现我所遇到的大多数解释都是相当难以理解的,并且缺乏实际细节。 回答1 首先:如果您不是数学家,那么monad一词会有些空洞。 一个替代术语是计算构建器,它更能描述它们的实际用途。 它们是链接操作的一种模式。 它看起来有点像面向对象语言中的方法链接,但是机制略有不同。 该模式主要用于功能语言(尤其是Haskell,它普遍使用monad),但可以用于支持高阶函数(即可以将其他函数作为参数的函数)的任何语言。 JavaScript中的数组支持该模式,因此让我们将其用作第一个示例。 模式的要点是我们有一个类型(在这种情况下为Array ,该类型具有以函数作为参数的方法。 提供的操作必须返回相同类型的实例(即返回Array )。 首先是不使用monad模式的方法链接示例: [1,2,3].map(x => x + 1) 结果是[2,3,4] 。 该代码不符合monad模式,因为我们作为参数提供的函数返回一个数字,而不是一个Array。 monad形式的相同逻辑是: [1,2,3].flatMap(x => [x + 1]) 在这里,我们提供了一个返回Array的操作,因此现在它符合该模式。 flatMap方法为数组中的每个元素执行提供的函数。 每次调用都希望有一个数组作为结果
  • 什么是单子?(What is a monad?)
    问题 最近对Haskell进行了简要介绍之后,关于monad本质上是什么的简要而简洁的实际解释是什么? 我发现我所遇到的大多数解释都是相当难以理解的,并且缺乏实际细节。 回答1 首先:如果您不是数学家,那么monad一词会有些空洞。 一个替代术语是计算构建器,它更能描述它们的实际用途。 它们是链接操作的一种模式。 它看起来有点像面向对象语言中的方法链接,但是机制略有不同。 该模式主要用于功能语言(尤其是Haskell,它普遍使用monad),但可以用于支持高阶函数(即可以将其他函数作为参数的函数)的任何语言。 JavaScript中的数组支持该模式,因此让我们将其用作第一个示例。 模式的要点是我们有一个类型(在本例中为Array ,该类型具有以函数作为参数的方法。 提供的操作必须返回相同类型的实例(即返回Array )。 首先是不使用monad模式的方法链接示例: [1,2,3].map(x => x + 1) 结果是[2,3,4] 。 该代码不符合monad模式,因为我们作为参数提供的函数返回一个数字,而不是一个Array。 monad形式的相同逻辑是: [1,2,3].flatMap(x => [x + 1]) 在这里,我们提供了一个返回Array的操作,因此现在它符合该模式。 flatMap方法为数组中的每个元素执行提供的函数。 每次调用都希望有一个数组作为结果(而不是单个值
  • Haskell的隐藏功能(Hidden features of Haskell [closed])
    问题 从目前的情况来看,这个问题不适合我们的问答形式。 我们希望答案得到事实,参考或专业知识的支持,但是这个问题可能会引起辩论,争论,民意测验或进一步的讨论。 如果您认为此问题可以解决并且可以重新提出,请访问帮助中心以获取指导。 9年前关闭。 已锁定。 该问题及其答案被锁定,因为该问题是题外话,但具有历史意义。 它目前不接受新的答案或互动。 Haskell编程语言鲜为人知但有用的功能是什么? (我知道这种语言本身鲜为人知,但是可以和我一起工作。即使我对Haskell中简单的事情的解释,例如用一行代码定义斐波那契数列,也会被我所接受。) 尝试将答案限制在Haskell核心每个答案一项功能给出该功能的示例和简短描述,而不仅仅是指向文档的链接使用粗体标题作为第一行标记功能 回答1 我的大脑刚刚爆炸 如果您尝试编译此代码: {-# LANGUAGE ExistentialQuantification #-} data Foo = forall a. Foo a ignorefoo f = 1 where Foo a = f 您将收到以下错误消息: $ ghc Foo.hs Foo.hs:3:22: My brain just exploded. I can't handle pattern bindings for existentially-quantified constructors
  • Haskell do clause with multiple monad types
    I'm using a graphic library in Haskell called Threepenny-GUI. In this library the main function returns a UI monad object. This causes me much headache as when I attempt to unpack IO values into local variables I receive errors complaining of different monad types. Here's an example of my problem. This is a slightly modified version of the standard main function, as given by Threepenny-GUI's code example: main :: IO () main = startGUI defaultConfig setup setup :: Window -> UI () setup w = do labelsAndValues <- shuffle [1..10] shuffle :: [Int] -> IO [Int] shuffle [] = return [] shuffle xs = do
  • 用Haskell方法对多个项目进行错误检查并中止(Haskell way to do error checking of multiple items with abort)
    问题 Haskell函数检查多个不同条件并在失败时返回错误消息的好方法是什么? 用Python或类似语言,将很简单: if failure_1: return "test1 failed" if failure_2: return "test2 failed" ... if failure_n: return "testn failed" do_computation 在Haskell中没有任意嵌套的case / if语句的情况下,如何做到这一点? 编辑:某些测试条件可能需要IO,这会将任何测试结果放入IO monad中。 我相信这会带来很多解决方案。 回答1 因此,您被困在IO ,并且想要检查一堆没有很多嵌套if的条件。 希望您能通过回答而偏离我对Haskell中更一般的问题的解决。 抽象地考虑这需要如何表现。 检查状况有以下两种结果之一: 成功,在这种情况下,程序将运行其余功能失败,在这种情况下,程序将放弃其余功能,并返回错误消息。 可以递归查看多个条件。 每次它运行“其余功能”时,它都会到达下一个条件,直到到达最后一个返回结果的步骤。 现在,作为解决问题的第一步,让我们使用该结构来分解事物-因此,基本上,我们希望将一堆任意条件分解为几部分,然后将它们组合为一个多条件函数。 关于这些作品的性质,我们可以得出什么结论? 1)每块都可以返回两种不同类型中的一种;
  • Scala 相当于 Haskell 的 where 子句?(Scala equivalent to Haskell's where-clauses?)
    问题 是否可以使用类似于 Scala 中的 where-clauses 的东西? 也许有一些我没有想到的技巧? 编辑: 感谢您的所有回答,非常感谢。 总结一下:local vars, vals 和 defs 可以用来实现几乎相同的事情。 对于惰性求值,可以使用惰性 val(带有隐式缓存)或函数定义。 确保功能纯度留给程序员。 现在只剩下一个问题:有没有办法将值或函数定义放在它们被使用的表达式之后? 有时这似乎更清楚。 这对于类或对象的字段/方法是可能的,但它似乎不适用于方法。 到目前为止,答案中没有提到的另一件事。 where-clauses 还限制了其中定义的表达式的范围。 我也没有找到在 Scala 中实现这一目标的方法。 回答1 在 Hakell 中,where 子句保存函数的局部定义。 Scala 没有显式的 where 子句,但可以通过使用局部var 、 val和def来实现相同的功能。 本地`var`和`val` 规模: def foo(x: Int, y: Int): Int = { val a = x + y var b = x * y a - b } 在哈斯克尔: foo :: Integer -> Integer -> Integer foo x y = a - b where a = x + y b = x * y 本地`def` 在斯卡拉 def foo(x
  • 被多个数据结构引用时更新记录(Updating record when referenced by multiple data structures)
    问题 假设我有一条记录,例如Person ,并且我希望能够通过多个数据结构查找此人。 也许有一个按名称的索引,一个按人的邮政编码的索引,以及另一个按人当前的纬度和经度的索引。 也许还有更多的数据结构。 所有这些之所以存在,是因为我需要有效地查找一个或多个具有不同条件的人员。 如果我只需要阅读一个人的属性,这是没有问题的。 但是现在假设我需要使用这些数据结构之一查找一个人,然后更新该人的数据。 在OOP语言中,每个数据结构将指向内存中的同一个人。 因此,当您更新一个时,您也在隐式地更新其他数据结构的引用。 这几乎是对副作用和杂质的定义。 我知道这完全与Haskell范式背道而驰,而且我不希望Haskell这样工作。 那么,Haskell式的实现方式是什么? 明确地说,问题是这样的:我通过一个数据结构查找一个人,然后将该人(可能还有其他一些任意数据)传递给ArbitraryData -> Person -> Person类型的函数。 如何在所有各种查找结构中传播此更改? 作为Haskell的一个相对较新的人,我的第一个直觉是每次我更新一个人时,都用新更新的人来重建每个查找结构。 但这似乎是很多仪式,但我有很多机会以GHC无法检测到的方式搞砸,而且一点也不优雅。 Haskell以其优雅而著称,我无法想象它缺乏解决此类常见和基本问题的优雅解决方案。 所以我想我缺少了一些东西。 作为参考
  • 为什么在Haskell中将副作用建模为monad?(Why are side-effects modeled as monads in Haskell?)
    问题 任何人都可以对为什么Haskell中的不纯计算被建模为monad提出一些建议吗? 我的意思是monad只是一个具有4种操作的接口,那么在其中建模副作用的原因是什么? 回答1 假设一个函数有副作用。 如果我们将其产生的所有效果作为输入和输出参数,则该函数对外界而言是纯净的。 因此,对于不纯净的功能 f' :: Int -> Int 我们将RealWorld添加到考虑中 f :: Int -> RealWorld -> (Int, RealWorld) -- input some states of the whole world, -- modify the whole world because of the side effects, -- then return the new world. 那么f是纯的。 我们定义了一个参数化的数据类型, type IO a = RealWorld -> (a, RealWorld) ,所以我们不需要多次键入RealWorld,而只需编写 f :: Int -> IO Int 对于程序员来说,直接处理RealWorld太危险了-特别是,如果程序员接触到RealWorld类型的值,他们可能会尝试复制它,这基本上是不可能的。 (例如,尝试复制整个文件系统。您将其放置在哪里?)因此,我们对IO的定义也封装了整个世界的状态。 “不纯”功能的组成
  • 一个monad只是endofunctors类别中的一个monoid,这是什么问题?(A monad is just a monoid in the category of endofunctors, what's the problem?)
    问题 谁先说以下? 一个monad只是endofunctors类别中的一个monoid,这是什么问题? 在一个不太重要的注解上,这是真的吗?如果可以的话,您能否给出一个解释(希望是一个没有太多Haskell经验的人可以理解的解释)? 回答1 詹姆斯·艾里(James Iry)的这种特别措辞来自他极富娱乐性的《简明,不完整和大部分错误的编程语言历史》 ,他在小说中将其归因于菲利普·沃德勒(Philip Wadler)。 原始报价摘自Saunders Mac Lane的《工作数学家的类别》,这是类别理论的基础文章之一。 在上下文中,这可能是确切了解其含义的最佳位置。 但是,我会刺。 原来的句子是这样的: 总而言之,X中的单子仅是X的终结者类别中的一个单义词,乘积×被终结者的组成所取代,单位由终结者的身份设定。 X这里是一个类别。 Endofunctors是从一个类别到其自身的Functor (就函数式程序员而言,通常是Functor的所有Functor ,因为它们主要只处理一个类别;类型的类别-但我离题了)。 但是您可以想象另一个类别,即“ X上的endofunctors”类别。 这是一类,其中的对象是内泛函子,而态射是自然变换。 在这些终结者中,有些可能是单子。 哪一个是单子? 确切地说,在特定意义上是单调的。 与其说出从单子到Monoid的确切映射(因为Mac
  • Haskell 中的主函数是否总是以 main = do 开头?(Does the main-function in Haskell always start with main = do?)
    问题 在java中我们总是这样写: public static void main(String[] args){...} 当我们要开始编写程序时。 我的问题是,Haskell 和 IE 是否相同:当我想在 Haskell 中为程序编写代码时,我是否可以始终确保声明:main = do? 例如: main = do putStrLn "What's your name?" name <- getLine putStrLn ("Hello " ++ name) 这个程序将询问用户“你叫什么名字?” 然后,用户输入将存储在名称变量中,并在程序终止之前显示“Hello”++ 名称。 回答1 简短回答:不,我们必须声明一个main = ,而不是一个do 。 main必须是IO monad类型(所以IO a ),其中a是任意的(因为它被忽略),如下所示: main名称的使用很重要: main被定义为 Haskell 程序的入口点(类似于 C 中的main函数),并且必须具有IO类型,通常是IO () 。 但是你不需要do符号。 其实做的是语法糖。 您的main内容实际上是: main = putStrLn "What's your name?" >> getLine >>= \n -> putStrLn ("Hello " ++ n) 或者更优雅: main = putStrLn "What
  • Haskell 相当于 C# 5 async/await(Haskell equivalent of C# 5 async/await)
    问题 我刚刚阅读了使用await和async关键字在 C# 5.0 中处理异步函数的新方法。 来自有关 await 的 C# 参考的示例: private async Task SumPageSizesAsync() { // To use the HttpClient type in desktop apps, you must include a using directive and add a // reference for the System.Net.Http namespace. HttpClient client = new HttpClient(); // . . . Task<byte[]> getContentsTask = client.GetByteArrayAsync(url); byte[] urlContents = await getContentsTask; // Equivalently, now that you see how it works, you can write the same thing in a single line. //byte[] urlContents = await client.GetByteArrayAsync(url); // . . . } Task<byte[]>表示将生成byte[
  • Haskell 和 F# 之间的主要区别是什么? [关闭](What are the primary differences between Haskell and F#? [closed])
    问题 关闭。 这个问题需要更加集中。 它目前不接受答案。 7年前关闭。 锁定。 此问题及其答案已锁定,因为该问题是题外话但具有历史意义。 它目前不接受新的答案或互动。 我在 Internet 上搜索了 F# 和 Haskell 之间的比较,但没有找到任何真正确定的内容。 主要区别是什么?为什么我要选择一个而不是另一个? 回答1 Haskell 是一种“纯”函数式语言,而 F# 具有命令式/OO 和函数式语言的方面。 Haskell 也有惰性求值,这在函数式语言中相当罕见。 这些东西是什么意思? 纯函数式语言,意味着没有副作用(或调用函数时共享状态的变化),这意味着您可以保证,如果调用 f(x),除了从函数返回值外,不会发生任何其他事情,例如控制台输出、数据库输出、对全局或静态变量的更改……尽管 Haskell 可以具有非纯函数(通过 monad),但它必须通过声明“显式”隐含。 纯函数式语言和“无副作用”编程最近很受欢迎,因为它非常适合多核并发,因为没有共享状态而不是无数的锁和信号量更难出错。 惰性求值是指在绝对必要之前不求值的函数。 这意味着在不需要时可以避免许多操作。 在基本的 C# if 子句中考虑这一点,例如: if(IsSomethingTrue() && AnotherThingTrue()) { do something; } 如果IsSomethingTrue()为
  • IO Monad 在什么意义上是纯的?(In what sense is the IO Monad pure?)
    问题 我已经将 IO monad 描述为 State monad,其中 state 是“现实世界”。 这种 IO 方法的支持者认为,这使得 IO 操作变得纯粹,就像引用透明一样。 这是为什么? 从我的角度来看,IO monad 中的代码似乎有很多可观察到的副作用。 此外,是否有可能描述几乎任何非纯函数,如现实世界的函数? 例如,我们不能把 C 的 malloc 想象成一个函数,它接受一个RealWorld和一个 Int 并返回一个指针和一个RealWorld ,就像在 IO monad 中一样, RealWorld是隐式的吗? 注意:我知道 monad 是什么以及它是如何使用的。 请不要回复随机 monad 教程的链接,除非它专门解决了我的问题。 回答1 我认为我听到的最好的解释实际上是最近关于 SO 的。 IO Foo是创建Foo的秘诀。 另一种常见的、更直接的说法是,它是一个“产生Foo程序”。 可以执行(多次)以创建Foo或尝试死亡。 配方/程序的执行是我们最终想要的(否则,为什么要写一个?),但在我们的代码中由IO操作表示的是配方本身。 该配方是一个纯值,与String是纯值完全相同。 配方可以以有趣的、有时令人惊讶的方式组合和操作,但是这些配方可以组合的多种方式(除了公然非纯粹的unsafePerformIO 、 unsafeCoerce等)都是完全参照透明的、确定性的
  • 为什么不能在 Java 中声明 Monad 接口?(Why can the Monad interface not be declared in Java?)
    问题 在开始阅读之前:这个问题不是关于理解 monad,而是关于识别 Java 类型系统的限制,它阻止了Monad接口的声明。 在我努力理解 monad 的过程中,我阅读了 Eric Lippert 在一个问题上的 SO-answer,该问题询问了对 monad 的简单解释。 在那里,他还列出了可以在 monad 上执行的操作: 有一种方法可以将未放大类型的值转换为放大类型的值。 有一种方法可以将未放大类型的操作转换为遵守前面提到的函数组合规则的放大类型的操作通常有一种方法可以将未放大的类型从放大类型中取出。 (最后一点对于 monad 并不是绝对必要的,但这种操作经常存在。) 在阅读了有关 monad 的更多信息后,我将第一个操作确定为return函数,将第二个操作确定为bind函数。 我找不到第三个操作的常用名称,因此我将其称为unbox函数。 为了更好地理解 monad,我继续尝试在 Java 中声明一个通用的Monad接口。 为此,我首先查看了上述三个函数的签名。 对于 Monad M ,它看起来像这样: return :: T1 -> M<T1> bind :: M<T1> -> (T1 -> M<T2>) -> M<T2> unbox :: M<T1> -> T1 return函数不在M的实例上执行,因此它不属于Monad接口。 相反,它将被实现为构造函数或工厂方法。
  • 关于“Learn you a Haskell”的 State Monad 代码的困惑(Confusion over the State Monad code on “Learn you a Haskell”)
    问题 我正在尝试使用在线书籍 Learn you a Haskell for great Good 来了解 Haskell。 据我所知,到目前为止,我已经能够理解 Monad,直到我看到介绍 State Monad 的章节。 但是,所提供并声称是 State 类型的 Monad 实现的代码(我无法在 Hoogle 中找到它)似乎对我来说太多了。 首先,我不明白它背后的逻辑,即为什么它应该起作用以及作者如何考虑这种技术。(也许可以建议相关文章或白皮书?) 在第 4 行,建议函数 f 接受 1 个参数。 然而,在接下来的几行中,我们会看到 pop,它不需要任何参数! 扩展第 1 点,作者试图通过使用函数来表示状态来完成什么。 非常感谢任何帮助理解正在发生的事情。 编辑 敬启者, 下面的答案彻底涵盖了我的问题。 不过,我想补充一件事: 在阅读了下面建议的文章后,我找到了上面第二点的答案:一直以来,我都认为pop 函数的使用方式如下: stuff >>= pop因为在绑定类型中第二个参数是函数,而正确的用法是这个pop >>= stuff ,我在再次阅读 do-notation 如何转换为普通的 bind-lambdas 后意识到。 回答1 简短的回答: State旨在利用 monad 的特性来模拟具有局部变量的类似命令式的系统状态。 基本思想是在 monad
  • Monad定律的解释(Explanation of Monad laws)
    问题 从对 Haskell 的温和介绍来看,有以下 monad 定律。 任何人都可以直观地解释它们的含义吗? return a >>= k = k a m >>= return = m xs >>= return . f = fmap f xs m >>= (\x -> k x >>= h) = (m >>= k) >>= h 这是我尝试的解释: 我们期望返回函数包装a以便它的一元性质是微不足道的。 当我们将它绑定到一个函数时,没有一元效应,它应该只将a传递给函数。 m的解包输出被传递给重新包装它的return 。 一元性质保持不变。 所以它和原来的 monad 是一样的。 解包的值被传递给f然后重新包装。 一元性质保持不变。 这是我们将普通函数转换为 monadic 函数时预期的行为。 我对这条法律没有解释。 这确实说明 monad 必须“几乎是关联的”。 回答1 你的描述看起来很不错。 一般来说,人们谈到三个单子定律,你有 1、2 和 4。你的第三个定律略有不同,我稍后会谈到。 对于三个 monad 定律,我发现使用 Kleisli 组合重写它们时,更容易直观地理解它们的含义: -- defined in Control.Monad (>=>) :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c mf >=> n = \x ->
  • 单子作为附件(Monads as adjunctions)
    问题 我一直在阅读关于范畴论中的单子。 单子的一个定义使用一对伴随函子。 monad 由使用这些函子的往返定义。 显然附属在范畴论中非常重要,但我还没有看到任何关于伴随函子的 Haskell monad 的解释。 有没有人考虑过? 回答1 编辑:只是为了好玩,我会正确地做到这一点。 原始答案保留在下面 当前类别附加的附加代码现在在附加包中:http://hackage.haskell.org/package/adjunctions 我只是要明确而简单地处理 state monad。 此代码使用来自 Transformers 包的Data.Functor.Compose ,但在其他方面是独立的。 f (D -> C) 和 g (C -> D) 之间的附加词,写作 f -| g, 可以通过多种方式进行表征。 我们将使用 counit/unit (epsilon/eta) 描述,它给出了两个自然变换(函子之间的态射)。 class (Functor f, Functor g) => Adjoint f g where counit :: f (g a) -> a unit :: a -> g (f a) 请注意,counit 中的“a”实际上是 C 中的恒等函子,而 unit 中的“a”实际上是 D 中的恒等函子。 我们还可以从 counit/unit 定义中恢复 hom-set
  • (已编辑)如何在没有 IO 的情况下在 Haskell 中获取随机数((Edited) How to get random number in Haskell without IO)
    问题 我想要一个函数,在没有 IO 的情况下在每次调用中返回不同的stdGen 。 我尝试使用unsafePerformIO ,如以下代码。 import System.IO.Unsafe import System.Random myStdGen :: StdGen myStdGen = unsafePerformIO getStdGen 但是当我尝试在 ghci 中调用myStdGen时,我总是得到相同的值。 我是否滥用了unsafePerformIO ? 或者还有其他方法可以达到我的目标吗? 编辑对不起,我想我应该更准确地描述我的问题。 实际上,我正在实现一种 treap 数据结构的变体,它需要一个特殊的“合并”操作。 它依赖于一些随机性来保证摊销 O(log n) 的预期时间复杂度。 我尝试使用像(Tree, StdGen)这样的一对来为每个陷阱保留随机生成器。 当向 treap 插入新数据时,我会使用random为新节点提供随机值,然后更新我的生成器。 但是我遇到了一个问题。 我有一个名为empty的函数,它将返回一个空的 treap,我使用上面的函数myStdGen来获取这个 treap 的随机生成器。 但是,如果我有两个空的 treap,它们的StdGen将是相同的。 因此,在我将数据插入到两个 treap 之后,当我想合并它们时,它们的随机值也将相同。 因此
  • GHC Haskell 目前的约束系统有什么问题?(What's wrong with GHC Haskell's current constraint system?)
    问题 我听说从 GHC 7.6 及以下版本开始,Haskell 的“损坏”约束系统存在一些问题。 它出什么问题了? 是否有可比较的现有系统来克服这些缺陷? 例如,edwardk 和 tekmo 都遇到了麻烦(例如来自 tekmo 的这条评论)。 回答1 好的,我在这里发帖之前与其他人进行了几次讨论,因为我想把它做对。 他们都告诉我,我描述的所有问题都归结为缺乏多态约束。 这个问题最简单的例子是MonadPlus类,定义为: class MonadPlus m where mzero :: m a mplus :: m a -> m a -> m a ...具有以下法律: mzero `mplus` m = m m `mplus` mzero = m (m1 `mplus` m2) `mplus` m3 = m1 `mplus` (m2 `mplus` m3) 请注意,这些是Monoid定律,其中Monoid类由下式给出: class Monoid a where mempty :: a mappend :: a -> a -> a mempty `mplus` a = a a `mplus` mempty = a (a1 `mplus` a2) `mplus` a3 = a1 `mplus` (a2 `mplus` a3) 那么为什么我们甚至有MonadPlus类呢? 原因是因为
  • Haskell入门(Getting started with Haskell)
    问题 这个问题的答案是社区的努力。 编辑现有答案以改善此职位。 它目前不接受新的答案或互动。 几天来,我一直试图围绕Haskell的函数式编程范例进行研究。 我通过阅读教程和观看截屏视频来做到这一点,但似乎没有任何坚持。 现在,在学习各种命令式/ OO语言(例如C,Java,PHP)时,练习已经成为我的理想选择。 但是由于我真的不太了解Haskell的功能,并且因为要使用许多新概念,所以我不知道从哪里开始。 那么,您是如何学习Haskell的? 是什么让您真正“破冰”? 另外,对于开始练习有什么好主意吗? 回答1 我将根据您在Haskell中的技能水平来订购本指南,从绝对的初学者到专家。 请注意,此过程将花费数月(数年?),因此相当长。 绝对的新人 首先,Haskell具有足够的技能,能够胜任任何工作。 它非常快(据我的经验仅落后于C和C ++),并且可用于从仿真到服务器,GUI和Web应用程序的任何内容。 但是,有些问题对于Haskell的初学者来说比其他问题更容易编写。 数学问题和列表处理程序是此的理想选择,因为它们只需要最基本的Haskell知识就可以编写。 学习Haskell的基础知识的一些很好的指南是“快乐学习Haskell教程”和“了解Haskell的伟大成就”(或其JupyterLab改编)的前6章。 在阅读这些内容的同时,用已知的知识解决简单的问题也是一个很好的主意