我正在学习Haskell,我发现它非常优雅和强大.但是我仍然无法使用OOP成像如何处理看起来如此简单的事情.以zope3/grok /金字塔网络平台为例.他们有一个基于匹配内容和视图来呈现页面的美妙理念.
在zope中,您有一个异类内容类型的树.当您请求URL时,您可以使用其到traverse该树的路径并获取context对象.路径的最后一部分是"视图名称".您可以View根据对象的类型和视图名称获得.它View是从特定上下文类型Request到Response对象的函数.
因此,如果您访问网址http://example.com/aFolder/aFaq/aQuestion/index.html,zope将从树的根目录开始.它会搜索一个名为节点aFolder,然后这个对象另一个名为内aFaq,和里面的一个命名aQuestion.因此该traverse函数可以返回任何类型的对象.
这里没问题.因为我只遍历树Traversable,所以我可以在Haskell中创建一个新的包装器类型或类,所以我会有一个Tree Traversable和一个函数traverse :: Tree Traversable -> [string] -> Traversable.
但后来出现问题,index.html是视图的名称.在一个简化的说明中[*] Zope寻找对(上下文的类型,viewname)并返回一个粗略的类型的函数{type of the context} -> Request -> Response.我可以编写一个函数render :: Traversable -> String -> Response来检查遍历的类型,但是,只要有人添加新的内容类型或新视图,就必须更新该函数.视图函数(或子函数)需要知道要使用其数据的上下文的类型.
那么,经验丰富的哈斯克勒如何攻击这类问题呢?我曾经想过GADT,但我不确定这是否会有所帮助,或者是否有更简单的选择.
谢谢!
编辑:伪代码澄清
def traverse(node, path):
# returns the context and the view name
itemname = path[0]
if hasattr(node, itemname):
# The next element in the path is a subnode of the node, let's visit it
return traverse(node[itemname], path[1:])
else:
# We can't go down the tree anymore, we found our context and view name
viewname = itemname
return node, viewname
def render(tree, request):
path = somehow_get_path_from_request(request)
context, viewname = traverse(tree, path)
# We get the view from a registry which is a map/dictionary
view = registry[(context, viewname)]
# here comes the problem:
# view is an object that knows exactly the type of context
# A view for a Question object can use its 'question' and 'answer' fields
# A View for a Folder can use its 'items' fields, a view for Image can use
# its 'img' field.
#
return view.render(context, request)
Run Code Online (Sandbox Code Playgroud)
这是我不知道怎么做在haskell.在haskell f我有一棵树,它必须是同质的物体.所以我必须定义一个包装类型Traversable.但是如果有人想要添加新类型,他应该修改我的代码.或者我可以创建一个haskell类Traversable.通过这种方式,可以将未来类型添加到树中.但是,我怎么能从(context,viewname)映射到未知上下文的函数?
[*]事实有点复杂.在zope中,一个对象或其类可以在运行时用任意标记interfaces(python没有接口的概念,这是一个完全由zope构成).接口形成一棵树.您将视图与对(接口,名称)相关联.当您请求(context,name)视图时,它返回与最特定接口关联的视图.我们的想法是您可以更改视图,更改接口注册表而不修改代码.
如果我理解这个权利,你的问题归结为"我要的东西未知类型的东西放入容器中,然后在运行时我想根据自己的类型来将它们拉出来并运行不同的功能".
有几种可能的方法来解决这个问题,具有不同程度的复杂性.
首先,Haskell有类型擦除.编译程序时,在编译器检查所有类型都正常后,它会擦除它们.因此,在运行时,不可能告诉任何类型的东西.这只能起作用,因为在编译时不可能不知道某事物的类型.这是一种冗长的说法"Haskell是静态类型的".
刚刚说过,"Haskell有一些扩展"可以做到这一点.但是,我怀疑你的问题可能有一个更简单的解决方案......
好的,所以你想存储不同类型的东西.但是,你真的想要做这个东西,一旦你存储呢?如果您只想对数据执行某些功能(取决于其类型),那么为什么不直接存储该功能呢?
想一想.它不是包含许多不同类型的容器,而是包含许多具有相同类型的函数.您获取所需的功能,然后运行它.完成.
那是什么?您需要能够在数据上运行多个不同的功能吗?那么,在这种情况下,请尝试存储包含所需功能的数据结构.