几天前,我的一个朋友给了我一个Haskell问题

Hey, MK,假设我有个BNF,并且我在Haskell中有个这个BNF的parser。
现在,我想给这个BNF改一行,有没有办法不用动这个BNF parser的代码(因为是其他人写的),而是对这parser进行扩展呢?

这问题挺有趣的,也不算难。

这问题说是extensibility problem,其实有两个地方需要扩展。

0:Parser需要用open recursion之类的方法扩展

1:Parse出来的ADT也需要可扩展性

后半个需求见多了,Final Tagless,DTALC,Tree that grow,Recursion scheme style fix。。。于是放下不表,我们来处理前一个。

前半个。。Haskell's Overlooked Object System就搞过,当然他们有点heavy weight,打算随手弄一个超级轻量级的:5行就够了,多一行是小莎莎。

Ready?

 data Object x = MkObject (x -> x)

1。Inheritance is not subtyping式的Object=recursive type。为了简易性(反正也不需要多高的扩展性)就不model真。recursive type,而只有recursive dependency。

 use :: Object x -> x
use (MkObject x) = let res = x res in res

2。3。最典型的tying the knot。其实就是fix了。

我们想想,这个x是什么variant的呢?covariant还是contravariant?

 inherit :: (a -> b) -> (b -> a) -> Object a -> Object b
inherit ab ba (MkObject aa) = MkObject (ab . aa . ba)

既然是invariant,那fmap contramap都用不上,但invariant依然能有map:两边一起传进来就行了。4。5。

这就是一个prototype based oo system了。

接下来讲怎么用哈:

 test :: Object (Int, Int)
test = MkObject $ \self -> (2, fst self + fst self)

这弄了个两个field的object,第零个field初始值为2(可能因为继承被override),第一个field为第零个field的值*2(不一定是3,如果任何field被override这个值都能改)。use test应该是(2, 4)。

 inheritTest :: Object ((Int, Int), Int)
inheritTest = inherit (\(l, r) -> ((l + 1, r + 2), r + 1)) fst test

这里继承了上面的Object,override了l(l + 1是super + 1),r被override到super + 2,加了个新的field,值是r+1。use inheritTest应该是((3, 8), 7)。记着传进来的参数不是self而是super就很好理解了。

好,open recursion搞好了,剩下的就是标准的final tagless了,体力活,没啥意思

 class AST repr where
lit :: Int -> repr
plus :: repr -> repr -> repr class Var repr where
var :: String -> repr type WholeParser repr = Parser repr
type LitParser repr = Parser repr
type PlusParser repr = Parser repr intP :: Parser Int
intP = read <$> many1 digit stringP :: Parser String
stringP = many1 letter type OriginalParser repr = ((LitParser repr, PlusParser repr), WholeParser repr)
originalParser :: AST repr => Object (OriginalParser repr)
originalParser = MkObject $ \(~(_, p)) -> let
litP = lit <$> intP
plusP = between (char '(') (char ')') (do {l <- p; spaces; char '+'; spaces; r <- p; return $ plus l r})
wholeP = litP <|> plusP in
((litP, plusP), wholeP) type VarParser repr = Parser repr
extendedParser :: (AST repr, Var repr) => Object (VarParser repr, OriginalParser repr)
extendedParser = inherit extend snd originalParser
where
extend ~((litP, plusP), wholeP) = let
varP = var <$> stringP in
(varP, ((litP, plusP), varP <|> wholeP)) instance AST String where
lit = show
plus x y = "(" ++ x ++ " " ++ "+" ++ " " ++ y ++ ")" instance Var String where
var x = x

大功告成。

代码在https://github.com/MarisaKirisame/extensible-parser/blob/master/src/Lib.hs

Q:封装呢?

A:Abstract Type is Existential Type

Q:这是prototype based的,class怎么办?

A:A Theory Of Object里面讲过怎么用prototype来做class

Q:多继承呢?

A:给定Object a,Object b,可以组合出Object (a, b),要菱形继承自己手动再inherit一下就好

Q:Subtyping?

A:Typeclass。

如果大家感兴趣,请评论下,我可以再写个blog把这些功能补完。

愿你走出半生,归来仍是Java Parser的更多相关文章

  1. CHAPTER 8 Out of Darkness 第8章 走出黑暗

    CHAPTER 8 Out of Darkness 第8章 走出黑暗 We expect scientists to be trying to discover new things, and for ...

  2. 用了Scrum越来越累?这三点帮你走出困境

    摘要:你有没有一种感觉,团队用了Scrum之后,工作任务越来越多,加班越来越严重?有?好兄弟,这篇文章正好能帮你~ 本文分享自华为云社区<用了Scrum越来越累?这三点帮你走出困境>,作者 ...

  3. ofo走出校园观察:市场定位导致产品错位?

    Ofo和摩拜单车虽然同样都是做单车共享,但实际上两者在最初的市场定位是有明显的差异的,因此提供的产品方案也存在巨大的差异. 市场定位不同,导致产品方案的巨大差异 摩拜单车一开始就定位于开放市场,充分的 ...

  4. 为什么DIY报价----走出软件作坊:三五个人十来条枪 如何成为开发正规军(十二)[转]

    前段时间,写了一个开发.实施.服务费用计算三部曲. 水清则无鱼--走出软件作坊:三五个人十来条枪 如何成为开发正规军(八) 实施费用也能DIY--走出软件作坊:三五个人十来条枪 如何成为开发正规军(九 ...

  5. 将服务费用DIY到底----走出软件作坊:三五个人十来条枪 如何成为开发正规军(十)[转]

    前一段时间,讲了一系列开发经理.实施经理.服务经理的工具箱:开发经理的工具箱---走出软件作坊:三五个人十来条枪 如何成为开发正规军(三) ,实施经理的工具箱--走出软件作坊:三五个人十来条枪 如何成 ...

  6. 走出测试,走向CEO

    飞测说:大家好,我们又见面了,我是黑夜小怪.不巧,今晚加班回来路上,湿身了,淋了个落汤鸡,不过明天也许可以看海了,也就呵呵了,原本想回来后聊些技术的,现在突然想先聊聊我的一些想法,仅供交流. 走出测试 ...

  7. Sql Server查询性能优化之走出索引的误区

    据了解绝大多数开发人员对于索引的理解都是一知半解,局限于大多数日常工作没有机会.也什么没有必要去关心.了解索引,实在哪天某个查询太慢了找到查询条件建个索引就ok,哪天又有个查询慢了,再建立个索引就是, ...

  8. 走出MFC子类化的迷宫

    走出MFC子类化的迷宫 KEY WORDS:子类化 SUBCLASSWINDOW  MFC消息机制 许多Windows程序员都是跳过SDK直接进行RAD开发工具[或VC,我想VC应不属于RAD]的学习 ...

  9. 在8X8的棋盘上分布着n个骑士,他们想约在某一个格中聚会。骑士每天可以像国际象棋中的马那样移动一次,可以从中间像8个方向移动(当然不能走出棋盘),请计算n个骑士的最早聚会地点和要走多少天。要求尽早聚会

    在8X8的棋盘上分布着n个骑士,他们想约在某一个格中聚会.骑士每天可以像国际象棋中的马那样移动一次,可以从中间像8个方向移动(当然不能走出棋盘),请计算n个骑士的最早聚会地点和要走多少天.要求尽早聚会 ...

随机推荐

  1. 在ASP.NET MVC应用中开发插件框架(中英对照)

    [原文] Developing a plugin framework in ASP.NET MVC with medium trust [译文] 在ASP.NET MVC应用中开发一个插件框架 I’v ...

  2. Vue + Bootstrap 制作炫酷个人简历(二)

    没想到隔了这么久才来更新. 用vue做简历,不是非常适合,为什么呢. 因为简历没什么数据上的操作,一般都是静态的内容. 不过都说了用Vue来做,也只能强行续命了. 这里是我做好的成品  非一般简历 由 ...

  3. VSTO:C#获取文档控件的值

    基础知识准备: VSTO入门 创建Excel解决方案   string[] inputfileNames = { @"C:\1.xls", @"C:\2.xls" ...

  4. Vue2.5开发去哪儿网App 第二章笔记

    Vue完成  TodoList 1.默认方式 <!DOCTYPE html> <html lang="en"> <head> <meta ...

  5. C#委托和事件例析

    我是对Java了解相对较多,而对C#则是因工作需要才去看了一下,C#跟Java在语法上非常相似,而最初让我比较困惑的就是委托.事件部分,相信大多数初学者也有类似的困惑.经过跟Java的对比学习,发现这 ...

  6. 【Canal源码分析】整体架构

    本文详解canal的整体架构. 一.整体架构 说明: server代表一个canal运行实例,对应于一个jvm instance对应于一个数据队列 (1个server对应1..n个instance) ...

  7. SQL Server性能优化(7)理解数据库文件组织

    一.基本单位"页"     SQL Server是用8KB的页来存储数据.物理I/O操作也是在页级执行.页的种类有很多,具体参考(MSDN).我们关注更多的是数据页的结构,包括三部 ...

  8. 【从0到1学Web前端】CSS定位问题二(float和display的使用) 分类: HTML+CSS 2015-05-28 22:03 812人阅读 评论(1) 收藏

    display 属性规定元素应该生成的框的类型. 这个属性用于定义建立布局时元素生成的显示框类型.对于 HTML 等文档类型,如果使用 display 不谨慎会很危险,因为可能违反 HTML 中已经定 ...

  9. Centos7下安装redis实战(单机版以及集群)

    一.背景 因项目需要,要引入redis做缓存,就在centos7下亲自安装了一遍redis,刚好趁着这个机会就来把redis的概念以及单机版和集群版redis安装步骤记录下来,在此和大家一起分享. 二 ...

  10. 详解C#特性和反射(二)

    使用反射(Reflection)使得程序在运行过程中可以动态的获取对象或类型的类型信息,然后调用该类型的方法和构造函数,或访问和修改该类型的字段和属性:可以通过晚期绑定技术动态的创建类型的实例:可以获 ...