Relens to Lens

Relens to Lens 之前也写过一篇lens的文章,学了新知识之后,现在看起来觉得不满意,于是打算重新再写一篇。 脚趾头:First Lens to Lenses Lens的设计十分的精妙,是数学指导编程的一个非常典型的案例。 Lens要解决的问题 在Haskell中,只提供了模式匹配的语法来操作数据,操作深层的是一个非常麻烦的事情。比如我们要操作以下数据: data Point = Point { positionX :: Double , positionY :: Double } deriving (Show, Eq) data Segment = Segment { segmentStart :: Point , segmentEnd :: Point } deriving (Show, Eq) p1 = Point 1 2 p2 = Point 3 4 l1 = Segment p1 p2 我们要修改l1的第二个端点的横坐标: l1 {segmentEnd = (segmentEnd l1) {positionX = 10}} -- 如果没有部分修改record的语法,这样的工作会更麻烦,只能用模式匹配一项项填 如果数据结构更加复杂的时候,代码就会变得更冗长。但如果用lens,刚刚的表达式就可以重写为:...

January 17, 2021 · 脚趾头 · 转自知乎

用Rust愉悦地编写Parser Combinator

本人有个小小的习惯,就是在学习一门语言的开始,为了熟悉这门语言的基础设施,我都会写一个最最简单的parserc(当然那些9012年都没有支持泛型的语言,就不写了)。 这次就试着用Rust来写一个。 从Iterator中来 用过Rust的Iterator的人,一定会觉得这用起来十分的愉悦,这说明Iterator设计得很不错。而parserc的使用方式其实和使用Rust的Iterator的方式十分相似的——先将小的组合子构造成大的组合子,然后再使用,parserc是.parse(),Iterator是.next()。 所以借鉴一下Iterator的思路,Rust版parserc也试着由这几部分构成: parser的trait 一些adapter 自定义一些combinator 组合出来的一些combinator 用起来大概是这样: // a+(b|c) fn aaab_c() -> impl Parser<Target=()> { char('a').some() .and(char('b').or(char('c'))) .map(|_| ()) } fn main() { let mut src = ParseState::new("aaab"); let res = aaab_c().parse(&mut src); assert_eq!(res, Some(())); } Parser trait 先定义parser的一般行为parse,“照搬”Iterator的结构。 #[derive(Clone, Debug)] pub struct ParseState<'a> { src: Chars<'a>, pub col: usize, pub row: usize, } pub trait Parser { type Target; fn parse<'a>(&self, state: &mut ParseState<'a>) -> Option<Self::Target>; } 在Haskell中,最简单的parse function的类型是String -> Maybe (a, String):吞进去一个String,得到解析结果a,还有未匹配的字符串,或者没有结果,也就是匹配失败。这里也采用了类似的结构,有所不同的是,因为Rust是有mut的,可以直接改变状态,于是可以去掉用返回值表示的状态,改为可变引用;然后,parse的状态加上了行和列,为了方便,用字符迭代器表示要解析的字符串。...

October 22, 2020 · 脚趾头 · 转自知乎