愿你走出半生,归来仍是Java Parser

几天前,我的一个朋友给了我一个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的更多相关文章
- CHAPTER 8 Out of Darkness 第8章 走出黑暗
CHAPTER 8 Out of Darkness 第8章 走出黑暗 We expect scientists to be trying to discover new things, and for ...
- 用了Scrum越来越累?这三点帮你走出困境
摘要:你有没有一种感觉,团队用了Scrum之后,工作任务越来越多,加班越来越严重?有?好兄弟,这篇文章正好能帮你~ 本文分享自华为云社区<用了Scrum越来越累?这三点帮你走出困境>,作者 ...
- ofo走出校园观察:市场定位导致产品错位?
Ofo和摩拜单车虽然同样都是做单车共享,但实际上两者在最初的市场定位是有明显的差异的,因此提供的产品方案也存在巨大的差异. 市场定位不同,导致产品方案的巨大差异 摩拜单车一开始就定位于开放市场,充分的 ...
- 为什么DIY报价----走出软件作坊:三五个人十来条枪 如何成为开发正规军(十二)[转]
前段时间,写了一个开发.实施.服务费用计算三部曲. 水清则无鱼--走出软件作坊:三五个人十来条枪 如何成为开发正规军(八) 实施费用也能DIY--走出软件作坊:三五个人十来条枪 如何成为开发正规军(九 ...
- 将服务费用DIY到底----走出软件作坊:三五个人十来条枪 如何成为开发正规军(十)[转]
前一段时间,讲了一系列开发经理.实施经理.服务经理的工具箱:开发经理的工具箱---走出软件作坊:三五个人十来条枪 如何成为开发正规军(三) ,实施经理的工具箱--走出软件作坊:三五个人十来条枪 如何成 ...
- 走出测试,走向CEO
飞测说:大家好,我们又见面了,我是黑夜小怪.不巧,今晚加班回来路上,湿身了,淋了个落汤鸡,不过明天也许可以看海了,也就呵呵了,原本想回来后聊些技术的,现在突然想先聊聊我的一些想法,仅供交流. 走出测试 ...
- Sql Server查询性能优化之走出索引的误区
据了解绝大多数开发人员对于索引的理解都是一知半解,局限于大多数日常工作没有机会.也什么没有必要去关心.了解索引,实在哪天某个查询太慢了找到查询条件建个索引就ok,哪天又有个查询慢了,再建立个索引就是, ...
- 走出MFC子类化的迷宫
走出MFC子类化的迷宫 KEY WORDS:子类化 SUBCLASSWINDOW MFC消息机制 许多Windows程序员都是跳过SDK直接进行RAD开发工具[或VC,我想VC应不属于RAD]的学习 ...
- 在8X8的棋盘上分布着n个骑士,他们想约在某一个格中聚会。骑士每天可以像国际象棋中的马那样移动一次,可以从中间像8个方向移动(当然不能走出棋盘),请计算n个骑士的最早聚会地点和要走多少天。要求尽早聚会
在8X8的棋盘上分布着n个骑士,他们想约在某一个格中聚会.骑士每天可以像国际象棋中的马那样移动一次,可以从中间像8个方向移动(当然不能走出棋盘),请计算n个骑士的最早聚会地点和要走多少天.要求尽早聚会 ...
随机推荐
- 20164317 《网络对抗技术》Exp6 信息搜集与漏洞扫描
Exp6 信息搜集与漏洞扫描 一.原理与实践说明 (1)实践原理 信息搜集:渗透测试中首先要做的重要事项之一,搜集关于目标机器的一切信息 间接收集 DNS记录扫描和枚举 CorpWatch:auxil ...
- 关于ajax访问webservice查询数据量稍微大一点,就会返回error500的解决方案
只需要在web.config的configeration节点中增加如下子节点即可: <system.web.extensions> <scripting> ...
- Python3.5 学习二十一
本节内容概要: 上节回顾及补充知识点: 一.请求周期: URL->路由->函数或类->返回字符串或者模板 Form表单提交: 提交->url-函数或者类中的方法 -....(执 ...
- iOS-项目开发1-Block
Block回顾 Block分为NSStackBlock, NSMallocBlock, NSGloblaBlock.即栈区Block,堆区Block,全局Block.在ARC常见的是堆块. 在ARC中 ...
- Swift5 语言指南(二十七) 访问控制
访问控制限制从其他源文件和模块中的代码访问部分代码.此功能使您可以隐藏代码的实现细节,并指定一个首选接口,通过该接口可以访问和使用该代码. 您可以为各个类型(类,结构和枚举)以及属于这些类型的属性,方 ...
- HTTP的请求报文与响应报文
报文: 简单来说,报文就是也就是HTTP报文,作用是在各个系统之间进行和响应时用来交换与传输的数据单元,即站点一次性要发送的数据块,这些数据块以一些文本形式的元信息开头,这些信息描述了报文的内容及含义 ...
- 剑指offer一之二维数组中的查找
一.题目: 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数. 二.解答方法: 方法 ...
- Ruby:Open-uri和Net::HTTP的不同
OpenURI不仅可以用来发起http请求,也可以发起https和ftp请求
- 使用安装 php-memcache-client
1.memcache:是一个高效的分布式内存对象缓存系统 2. IES---请求--->服务器(apace) | | |---->会查看memcache.是否有IES想要的内容--> ...
- 【转】谷歌三大核心技术(三)Google BigTable中文版
谷歌三大核心技术(三)Google BigTable中文版 摘要 Bigtable 是一个分布式的结构化数据存储系统,它被设计用来处理海量数据:通常是分布在数千台普通服务器上的PB级的数据.Goo ...