几天前,我的一个朋友给了我一个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. 【C#】简单的发送socket字符串

    1 打开VS,新建一个C#窗口程序 2 添加按钮 3 写按钮的事件代码 双击这个按钮 进入代码界面 输入如下内容,注意IP和端口 private void button1_Click(object s ...

  2. 定时任务 Wpf.Quartz.Demo.3

    先把全部源码上传,只是一个Demo,希望大家指点一下不足之处,见本文底部. 1.设置界面 2.详情页面 好了,现在慢慢叙述里面的一些方法. 3.实现拷贝的方法: (1) public static v ...

  3. 背水一战 Windows 10 (49) - 控件(集合类): Pivot, Hub

    [源码下载] 背水一战 Windows 10 (49) - 控件(集合类): Pivot, Hub 作者:webabcd 介绍背水一战 Windows 10 之 控件(集合类) Pivot Hub 示 ...

  4. 【转】Windows Server 2016 安装 IIS 服务时提示指定备用源路径

    原文地址:http://www.codingwhy.com/view/973.html 在Windows Serever 2016中安装IIS的时候,遇到以下提示 是否需要指定备用源路径?一个或多个安 ...

  5. 配置iSCSI部署网络存储

    iSCSI( Internet Small Computer System Interface 互联网小型计算机系统接口)是由IBM 下属的两大研发机构一一加利福尼亚AImaden和以色列Haifa研 ...

  6. Git - 回滚与撤销

    必要的概念 当前编辑界面:工作区(workspace) "git add"命令:将改动加入到缓存区(Index) "git commit"命令:提交代码到本地库 ...

  7. java里面main函数为什么要用static修饰

    这学期刚开java,因为之前只写过C++和Python没接触过java,有些写法挺不习惯的,今天写完一个程序,run的时候发现提示the selection can't be launched.... ...

  8. odoo datetime 直接修改模版语言 去掉时分秒

    <field name='date_order' widget='date'/> 利用date widget即可使dateime类型的显示为date.

  9. (转)python WSGI框架详解

    原文:https://www.cnblogs.com/shijingjing07/p/6407723.html?utm_source=itdadao&utm_medium=referral h ...

  10. Windows7下无法打开chm(mk:@MSITStore:路径[cannot open the file mk@MSITstore:路径]),chm索引就关闭的解决办法

    解决方法1是: 1,右键关联chm文件的“打开方式”到\Windows\HH.exe 2,在命令行运行regsvr32 itss.dll 3,在命令行运行regsvr32 hhctrl.ocx 方法2 ...