do notation

Reverse Polish notation(逆波兰式) Reverse Polish notation(逆波兰式) 介绍 逆波兰式(也叫后缀表达式)是一种将算数表达式的运算符写在操作数后面的表示方法。例如,在传统的波兰式(中缀表达式)中,表达式 (1+2)*(5+4) 在逆波兰式中可以表示为 1 2 + 5 4 + * 。逆波兰式的优点之一是它是无括号。 逆波兰式的计算 新建一个表达式,如果当前字符为变量或者为数字,则压栈,如果是运算符,则将栈顶两个元... 阅读详情

Using do blocks as an alternative monad syntax was first introduced way back in the Simple input and output chapter. There, we used do to sequence input/output operations, but we hadn't introduced monads yet. Now, we can see that IO is yet another monad.

Since the following examples all involve IO, we will refer to the computations/monadic values as actions (as we did in the earlier parts of the book). Of course, do works with any monad; there is nothing specific about IO in how it works.

Translating the then operator[edit]

The (>>) (then) operator works almost identically in do notation and in unsugared code. For example, suppose we have a chain of actions like the following one:

putStr "Hello" >> 
putStr " " >> 
putStr "world!" >> 
putStr "\n"

We can rewrite that in do notation as follows:

do putStr "Hello"
   putStr " "
   putStr "world!"
   putStr "\n"

This sequence of instructions nearly matches that in any imperative language. In Haskell, we can chain any actions as long as all of them are in the same monad. In the context of the IO monad, the actions include writing to a file, opening a network connection, or asking the user for input.

Here's the step-by-step translation of do notation to unsugared Haskell code:

do action1
   action2
   action3

becomes

action1 >>
do action2
   action3

and so on, until the do block is empty.

Translating the bind operator[edit]

The (>>=) is a bit more difficult to translate from and to do notation. (>>=) passes a value, namely the result of an action or function, downstream in the binding sequence. do notation assigns a variable name to the passed value using the <-.

do x1 <- action1
   x2 <- action2
   action3 x1 x2

x1 and x2 are the results of action1 and action2. If, for instance, action1 is an IO Integer then x1 will be bound to an Integer. The stored values are passed as arguments to action3, which returns a third action. The doblock is broadly equivalent to the following vanilla Haskell snippet:

action1 >>= \ x1 -> action2 >>= \ x2 -> action3 x1 x2

The second argument of (>>=) is a function specifying what to do with the result of the action passed as first argument. Thus, chains of lambdas pass the results downstream. Remember that, without extra parentheses, a lambda extends all the way to the end of the expression. x1 is still in scope at the point we call action3. We can rewrite the chain of lambdas more legibly by using separate lines and indentation:

action1
  >>=
    \ x1 -> action2
      >>=
        \ x2 -> action3 x1 x2

That shows the scope of each lambda function clearly. To group things more like the do notation, we could show it like this:

action1 >>= \ x1 ->
  action2 >>= \ x2 ->
    action3 x1 x2

These presentation differences are only a matter of assisting readability.[1]

The fail method[edit]

Above, we said the snippet with lambdas was "broadly equivalent" to the do block. The translation is not exact because the do notation adds special handling of pattern match failures. When placed at the left of either <- or ->, x1and x2 are patterns being matched. Therefore, if action1 returned a Maybe Integer we could write a do block like this...

do Just x1 <- action1
   x2      <- action2
   action3 x1 x2

...and x1 be an Integer. In such a case, what happens if action1 returns Nothing? Ordinarily, the program would crash with an non-exhaustive patterns error, just like the one we get when calling head on an empty list. With donotation, however, failures are handled with the fail method for the relevant monad. The do block above translates to:

action1 >>= f
where f (Just x1) = do x2 <- action2
                       action3 x1 x2
      f _         = fail "..." -- A compiler-generated message.

What fail actually does depends on the monad instance. Though it will often rethrow the pattern matching error, monads that incorporate some sort of error handling may deal with the failure in their own specific ways. For instance,Maybe has fail _ = Nothing; analogously, for the list monad fail _ = [].[2]

The fail method is an artifact of do notation. Rather than calling fail directly, you should rely on automatic handling of pattern match failures whenever you are sure that fail will do something sensible for the monad you are using.

Example: user-interactive program[edit]

Note

We are going to interact with the user, so we will use putStr and getLine alternately. To avoid unexpected results in the output, we must disable output buffering when importing System.IO. To do this, put hSetBuffering stdout NoBuffering at the top of your code. To handle this otherwise, you would explicitly flush the output buffer before each interaction with the user (namely a getLine) using hFlush stdout. If you are testing this code with ghci, you don't have such problems.


Consider this simple program that asks the user for their first and last names:

nameDo :: IO ()
nameDo = do putStr "What is your first name? "
            first <- getLine
            putStr "And your last name? "
            last <- getLine
            let full = first ++ " " ++ last
            putStrLn ("Pleased to meet you, " ++ full ++ "!")

A possible translation into vanilla monadic code:

nameLambda :: IO ()
nameLambda = putStr "What is your first name? " >>
             getLine >>= \ first ->
             putStr "And your last name? " >>
             getLine >>= \ last ->
             let full = first ++ " " ++ last
             in putStrLn ("Pleased to meet you, " ++ full ++ "!")

In cases like this, where we just want to chain several actions, the imperative style of do notation feels natural and convenient. In comparison, monadic code with explicit binds and lambdas is something of an acquired taste.

Notice that the first example above includes a let statement in the do block. The de-sugared version is simply a regular let expression where the in part is whatever follows from the do syntax.

Returning values[edit]

The last statement in do notation is the overall result of the do block. In the previous example, the result was of the type IO (), i.e. an empty value in the IO monad.

Suppose that we want to rewrite the example but return an IO String with the acquired name. All we need to do is add a return:

nameReturn :: IO String
nameReturn = do putStr "What is your first name? "
                first <- getLine
                putStr "And your last name? "
                last <- getLine
                let full = first ++ " " ++ last
                putStrLn ("Pleased to meet you, " ++ full ++ "!")
                return full

This example will "return" the full name as a string inside the IO monad, which can then be utilized downstream elsewhere:

greetAndSeeYou :: IO ()
greetAndSeeYou = do name <- nameReturn
                    putStrLn ("See you, " ++ name ++ "!")

Here, nameReturn will be run and the returned result (called "full" in the nameReturn function) will be assigned to the variable "name" in our new function. The greeting part of nameReturn will be printed to the screen because that is part of the calculation process. Then, the additional "see you" message will print as well, and the final returned value is back to being IO ().

If you know imperative languages like C, you might think return in Haskell matches return elsewhere. A small variation on the example will dispel that impression:

nameReturnAndCarryOn = do putStr "What is your first name? "
                          first <- getLine
                          putStr "And your last name? "
                          last <- getLine
                          let full = first++" "++last
                          putStrLn ("Pleased to meet you, "++full++"!")
                          return full
                          putStrLn "I am not finished yet!"

The string in the extra line will be printed out because return is not a final statement interrupting the flow (as it would be in C and other languages). Indeed, the type of nameReturnAndCarryOn is IO (), — the type of the finalputStrLn action. After the function is called, the IO String created by the return full will disappear without a trace.

Just sugar[edit]

As a syntactical convenience, do notation does not add anything essential, but it is often preferable for clarity and style. However, do is never used for a single action. The Haskell "Hello world" is simply:

main = putStrLn "Hello world!"

Snippets like this one are totally redundant:

fooRedundant = do x <- bar
                  return x

Thanks to the monad laws, we can and should write simply:

foo = bar

A subtle but crucial point relates to function composition: As we already know, the greetAndSeeYou action in the section just above could be rewritten as:

greetAndSeeYou :: IO ()
greetAndSeeYou = nameReturn >>= \ name -> putStrLn ("See you, " ++ name ++ "!")

While you might find the lambda a little unsightly, suppose we had a printSeeYou function defined elsewhere:

printSeeYou :: String -> IO ()
printSeeYou name = putStrLn ("See you, " ++ name ++ "!")

Now, we can have a clean function definition with neither lambdas or do:

greetAndSeeYou :: IO ()
greetAndSeeYou = nameReturn >>= printSeeYou

Or, if we have a non-monadic seeYou function:

seeYou :: String -> String
seeYou name = "See you, " ++ name ++ "!"

Then we can write:

-- Reminder: fmap f m == m >>= return . f == liftM f m
greetAndSeeYou :: IO ()
greetAndSeeYou = fmap seeYou nameReturn >>= putStrLn

Keep this last example with fmap in mind; we will soon return to using non-monadic functions in monadic code, and fmap will be useful there.

Notes

  1. ↑ Actually, the indentation isn't needed in this case. This is equally valid:
    action1 >>= \ x1 ->
    action2 >>= \ x2 ->
    action3 x1 x2
    
    

    Of course, we could use even more indentation if we wanted. Here's an extreme example:

    action1
      >>=
        \
          x1
            ->
              action2
                >>=
                  \
                    x2
                      ->
                        action3
                          x1
                          x2
    

    While that indention is certainly overkill, it could be worse:

    action1
      >>= \
        x1
          -> action2 >>=
            \
              x2 ->
                action3 x1
                  x2
    

    That is valid Haskell but is baffling to read; so please don't ever write like that. Write your code with consistent and meaningful groupings.

  2. ↑ This explains why, as we pointed out in the "Pattern matching" chapter, pattern matching failures in list comprehensions are silently ignored.

   https://en.wikibooks.org/wiki/Haskell/do_notation
do-notation:单子和索引单子的广义do表示法 做记号 | 奉献精神 我刚刚锁上了一扇敞开的门。奇怪,但具有象征意义。 曼妮&middot;卡拉维拉(Ganny Fandango) 概述 您是否曾经想过使用索引式monad管理复杂的不变量而又不放弃常规的monad? do-notation可以使您在幕后摆放一堆类型的jiggery-pokery。 它还提供了Ix m索引的monad,它是常规monad m的免费构造。凉爽的。 用法 {-# LANGUAGE RebindableSyntax #-} import Language.Haskell.DoNotation import Prelude hiding ( Monad ( .. ), pure ) 局限性 该实现与通过let绑定的do blocks不能很好地配合使用。 谢谢 向我大声喊叫,非常耐心地我完成了使所有这些工作都必需的不连贯的实例机制。 立即下载

相关推荐

CUDA编译踩坑实录:如何正确设置-gencode参数适配你的GPU算力

本文深入解析CUDA编译中-gencode参数的核心原理与实战配置,帮助开发者解决常见的'Unsupported gpu architecture'错误。通过厘清虚拟架构与真实架构的区别,并提供多版本兼容的CMake配置方案,确保编译出的代码能精准适配不同GPU算力,兼顾性能与广泛兼容性。

weixin_28829903的博客 338

算法与数据结构复习 第三章 栈与队列(详解)

文章目录第三章 栈与队列书面作业一、判断题二、单选题三、程序填空四、函数题 第三章 栈与队列 书面作业 一、判断题 1、在对不带头结点的链队列作出队操作时,不会改变头指针的值。 (F) 2、循环队列执行出队操作时会引起大量元素的移动。 (F) 3、不论是入队列操作还是入栈操作,在顺序存储结构上都需要考虑"溢出"情况。 (T) 4、“Circular Queue” is defined to be ...

好学者的博客 1万+

CAN:CANape使用Function处理数据

CANape中的function and script习惯称之为脚本(实际为函数和脚本)。这里介绍function,可以实现简单信号a和信号b乘法,比如计算功率(转速与扭矩乘)。该功能前期有专业人员做支援,当时问其编制方法,其回答是看帮助文件。现在没有支援自己动手。 功能描述:电压信号阶梯增加,每次保持1.5s,测量后0.5s位移平均值(假设信号步长10ms,需要取样50个点,但该脚本中点的数目为10个)。图示如下: 刚开始看了软件Function definition→Library function,

Morven 1万+

LeetCode: Evaluate Reverse Polish Notation(计算逆波兰表达式)两种方法

题目描述Evaluate the value of an arithmetic expression in Reverse Polish Notation.Valid operators are +, -, *, /. Each operand may be an integer or another expression.Some examples: ["2", "1", "+", "3", ...

山鬼谣弋痕夕的博客 612

MPC-CBF 控制原理详解

假设系统满足如下离散时间动力学模型xt1fxtutxt∈Xut∈Uxt1​fxt​ut​xt​∈Xut​∈Uxtx_txt​:状态变量,定义域为可行状态集X⊆RnX⊆Rnutu_tut​:控制输入,定义域为控制约束集U⊆RmU⊆Rm系统需满足安全约束,由如下控制障碍函数(CBF)hX→RhX→RCx∈X∣hx≥0C:=x∈X∣hx≥。

m0_54691290的博客 1179

7-1 链式基数排序 (20 分)

7-1 链式基数排序 (20 分) 实现链式基数排序,待排序关键字n满足1≤n≤1000,最大关键字数位≤5。 输入样例: 第一行输入待排序个数n(1≤n≤1000),再输入n个数(n的数位≤5)。 10 278 109 63 930 589 184 505 269 8 83 输出样例: 输出每趟分配-收集后链表中的关键字,趟数为序列中最大值的数位(如样例中930的数位为3),每行结尾有空格。 930 63 83 184 505 278 8 109 589 269 505 8 109 930 63 26

OwemShu的博客 4236

150. Evaluate Reverse Polish Notation(逆波兰式)

题目链接:https://leetcode.com/problems/evaluate-reverse-polish-notation/ 题意: Evaluate the value of an arithmetic expression in Reverse Polish Notation. Valid operators are +, -, *, /. Each operan

patkritLee的博客 616

从零搭建IoT数据中台:STM32+WebSocket实现边缘设备与Web端实时双向通信

本文详细介绍了如何从零搭建基于STM32和WebSocket协议的IoT数据中台,实现边缘设备与Web端的实时双向通信。通过硬件选型、协议设计、服务器搭建及安全优化,提供了一套低延迟、高并发的企业级解决方案,适用于智能农业和工业监控等场景。

tea88的博客 748

Haskell do notation 的正经定义

永远在用do,永远记不得do作为一个语法糖,对应的真实语法是什么。写一篇博文记一下。 -- | ---------------------------------------------------- -- | (1) do { m1 } ==> m1 -- | (2) do { m1; m2 } ==> m1 >> do { m2 } -- | (3) do { let s1; m1 s1 } ==> let s1 in do { m1 s1 } -- | (3’) do {

WinterShiver 916

【即插即用模块】卷积篇 | CVPR 2025 | LSConv:大核捕全局,小核提速度,替换 3×3 卷积/自注意力,精度效率双飙升

文章摘要: LSConv是一种新型卷积模块,采用"大核感知+小核聚合"策略,在轻量级视觉任务中表现出色。该模块由两部分组成:7×7深度可分离卷积(LKP)捕捉全局信息,动态3×3卷积(SKA)实现高效特征聚合。相比传统卷积和自注意力机制,LSConv在降低计算量的同时提升了精度,尤其适合高分辨率视觉任务。实验表明,在轻量化模型中用LSConv替代3×3卷积或自注意力,可显著减少计算量并提高准确率。模块支持即插即用,提供Triton优化实现,可直接集成到现有卷积或Transformer架构

主要更新底层视觉(去噪、超分等)相关的科研内容,形式为【论文精读】+【论文复现】 1119

Fluent验证案例03:管道中湍流流动压降计算——采用kw-sst模型计算压降更为准确。

阅读(4053) 评论(0)本案例利用Fluent计算管道中湍流流动压降。

qq_15963745的博客 876

Haskell风Do-notation实现——JavaScript中的Monad探索

Haskell风Do-notation实现——JavaScript中的Monad探索 1. 项目基础介绍 burrido 是一个开源项目,它尝试将 Haskell 编程语言中的 Do-notation 语法带到 JavaScript 中。该项目使用 JavaScript 作为主要的编程语言,通过引入 Monad 概念,让开发者可以用更函数式的方式处理异步和组合操作。 2. 核心功能 项目的核心功能...

gitblog_00120的博客 429

BES2700的PMU

BES2700ZP-BES2700芯片的PMU架构分析摘要:本文档分析了BES2700芯片的电源管理单元(PMU)架构。实际应用中仅需为VBAT引脚供电,其余电源均由芯片内部PMU提供。文档包含架构示意图、问题列表和参考链接,涉及PMU工作原理、外部供电要求等关键技术点。待解决问题包括具体供电参数和内部电源转换机制等。通过添加日志信息和代码片段,为开发人员提供了调试PMU功能的实用方法。

云淡风轻 117

fp-ts Do Notation 实战指南:用 bind / bindTo / apS 优雅地组合 effectful 代码

fp-ts 在 TypeScript 中实现了函数式编程所需的全部抽象(Monad、Applicative、Functor 等)。本篇指南围绕 [docs/guides/do-notation.md](https://link.gitcode.com/i/62e28ccfe51af2d1cdfb083836639ea8) 展开,系统讲解 fp-ts 自带的 do notation 实现:如何用 `

gitblog_00918的博客 163

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

fp-ts Zero 模块全解析:zero 抽象、guard 守卫与 do notation 中的条件分支

> **导读**:本文围绕 fp-ts 的 `Zero` 模块(`docs/modules/Zero.ts.md`)展开,系统讲解"零值抽象"(`zero`)与"条件守卫"(`guard`)两个核心概念。该模块从 v2.11.0 起引入,为 `Option`、`Array`、`ReadonlyArray`、`IOOption`、`TaskOption` 等常用数据类型提供了统一的"失败/空值"表示

gitblog_00360的博客 459

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

fp-ts Do Notation 速查与实战:用 bind/tap/map 写出类 Haskell 的 monad 链式代码

fp-ts 在 TypeScript 中为 monad 提供了 `Do`、`bind`、`tap`、`map` 等一整套 do notation 工具,让原本需要多层嵌套 `flatMap`/`pipe` 的效应式代码变得扁平、可读,与 Haskell、PureScript 的 `do` 语法糖一一对应。本文以仓库根目录的 [Do.md](https://link.gitcode.com/i/d0

gitblog_00548的博客 268

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

haskell - interactive evaluation at the prompt

Like most other language, haskell also has an interative prompt where you use repl way to quickly run and test your code. however, since haskell do no thave the statement ending sign such as the ; w...

iteye_13882的博客 229

Returns - 类型安全的功能性编程工具包

一、关于 Returns 项目 1、项目概览 2、相关链接资源 3、功能特性 二、安装配置 三、核心容器 1、Maybe 容器 2、RequiresContext 容器 3、Result 容器 4、IO 容器 5、Future 容器 四、进阶用法 1、组合操作 2、do-notation 五、参考文献

AI工程化、开源分享、文档翻译、代码笔记 953

returns与mypy完美集成:类型检查最佳实践指南

在Python开发中,类型安全是确保代码质量的关键因素。**returns**库与**mypy**的完美集成为开发者提供了一套强大的类型检查解决方案,让你的函数返回值既有意义又类型安全! ## 🚀 为什么需要mypy插件? 传统的Python类型检查存在诸多限制,特别是在函数式编程场景下。**returns**的mypy插件通过以下方式解决这些问题: - **增强类型推断**:为curry

gitblog_00614的博客 883

reduce 一个 笛卡尔积_[JavaScript] 求解任意n个集合的笛卡尔积

假设我们有两个集合,A={1, 2, 3},B={'a', 'b'},我们怎样求解这两个集合的笛卡尔积呢?A × B = {(1, 'a'), (1, 'b'), (2, 'a'), (2, 'b'), (3, 'a'), (3, 'b')}我们想到Haskell中的list comprehension正好有这种功能。(1)list comprehensionghci> [(x, y) | ...

weixin_29943753的博客 461

fp-ts IOOption 完全指南:用 Option 语义封装同步副作用计算

`IOOption<A>` 是 fp-ts 中表示"要么产出一个类型为 `A` 的值、要么什么都不产出"的同步计算类型。本指南以 [docs/modules/IOOption.ts.md](https://link.gitcode.com/i/db0c888f86c47f69894b7b7690030ad4) 为骨架,结合 [src/IOOption.ts](https://link.gitcod

gitblog_00042的博客 344

fp-ts 2.x 版本演进全览:从 2.0 到 2.16 的 CHANGELOG 深度导读

fp-ts(Functional programming in TypeScript)是 TypeScript 生态中最具代表性的函数式编程库之一,其 CHANGELOG.md(仓库根目录下)完整记录了从 2.0.0 到 2.16.11 的全部版本变更,是理解该库 API 设计哲学、命名约定与演进方向的一手资料。本文以这份 3375 行的变更日志为骨架,结合仓库 `src/` 源码与 `test/

gitblog_00568的博客 726

fp-ts IO 模块完全指南:用纯函数封装同步副作用计算

`IO<A>` 是 fp-ts 中描述"同步、可能产生副作用、但**绝不失败**"的计算的基石类型。本文以 [docs/modules/IO.ts.md](https://link.gitcode.com/i/365a242e443c0f98db4e346c1ab947dd) 文档为主线,结合 [src/IO.ts](https://link.gitcode.com/i/2b9c3fed2abd5

gitblog_00730的博客 184

fp-ts 的 Monad 类型类:从 Chain 与 Applicative 的组合到 Monad1~Monad4 接口体系

> 本文围绕 fp-ts 仓库中的 [Monad.ts 模块文档](https://link.gitcode.com/i/688720a189a1c4491c264c8543bff910) 展开,系统讲解 `Monad` 类型类的定位、律法(laws)、七种接口形态(`Monad`、`Monad1`、`Monad2`、`Monad2C`、`Monad3`、`Monad3C`、`Monad4`),并

gitblog_01007的博客 164

从 PureScript / Haskell 迁移到 fp-ts:函数式 TypeScript 概念对照实战指南

本指南面向已有 [PureScript](http://www.purescript.org/) 或 [Haskell](https://www.haskell.org/) 使用经验的开发者,系统梳理这两门语言的核心函数式编程概念在 TypeScript 生态下的对应写法,并以此前你熟悉的 `fp-ts` 实现作为对照蓝本。读完本文,你将掌握:如何用 `fp-ts` 的 pipe 链与 do no

gitblog_00619的博客 314

fp-ts ReaderIO 完全指南:用只读环境组合同步副作用

本文围绕 fp-ts 2.x 中在 v2.13.0 引入的 `ReaderIO` 模块展开,讲解如何将 `Reader` 的只读环境注入能力与 `IO` 的同步副作用能力组合为单一计算类型,并覆盖其全部构造器、转换、组合子、do notation、实例与遍历工具。读完本文,你将能熟练地用 `ReaderIO` 编写依赖配置环境、同时执行同步副作用(日志、随机数、时间等)的纯函数式 TypeScri

gitblog_00200的博客 942
上一篇: maven下载依赖
下一篇: How to use Spring’s WebApplicationInitializer
DViewer
博客等级 码龄16年 16粉丝 6原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值