Note that there are some explanatory texts on larger screens.

plurals
  1. POInvoke a function dynamically based on the type of one parameter (was "Zope in Haskell")
    text
    copied!<p>I'm learning Haskell and I find it very elegant and powerful. But I still have trouble imaging how to do things that seem so simple using OOP. Take for example the zope3/grok/pyramid web platforms. They have a beautiful philosophy based on matching content and views to render pages.</p> <p>In zope, you have a tree of heterodox content types. When you request an URL you use its path to <code>traverse</code> that tree and get a <code>context</code> object. The last part of the path is the "view name". You get the <code>View</code> based on the type of the object and the view name. The <code>View</code> is a function from the specific context type and the <code>Request</code> to a <code>Response</code> object.</p> <p>So if you visit the url <a href="http://example.com/aFolder/aFaq/aQuestion/index.html" rel="nofollow">http://example.com/aFolder/aFaq/aQuestion/index.html</a>, zope will start at the root of the tree. It will search for a node named <code>aFolder</code>, and then inside this object for another named <code>aFaq</code>, and inside it for one named <code>aQuestion</code>. So the <code>traverse</code> function could return an object of any type.</p> <p>There's no problem here. Since I'm only traversing the tree I can create a new wrapper type or a class in Haskell named <code>Traversable</code>, so I would have a <code>Tree Traversable</code> and a function <code>traverse :: Tree Traversable -&gt; [string] -&gt; Traversable</code>.</p> <p>But then comes a problem, <code>index.html</code> is the name of a view. In a simplified exposition[*] Zope looks for the pair (type of context, viewname) and returns a function, roughly speaking, of type <code>{type of the context} -&gt; Request -&gt; Response</code>. I could write a function <code>render :: Traversable -&gt; String -&gt; Response</code>, that checks the type of the traversable, but then, anytime that someone add a new content type or a new view, that function would have to be updated. The view function (or a sub-function) needs to know the type of the context to use its data.</p> <p><strong>So, how a seasoned haskeller attacks this type of problems? For a moment I thought in GADT, but I'm not sure if that will help or if there are more simple alternatives.</strong></p> <p>Thanks!</p> <p>EDIT: Pseudocode to clarify</p> <pre><code>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) </code></pre> <p>This is what I don't know how to do in haskell. In haskell f I have a tree, it must be of homogeneous objects. So I have to define a wrapper type Traversable. But then if someone wants to add a new type he should have to modify my code. Or I could create a haskell class Traversable. In this way an future type could be added to the tree. But then how could I map from (context, viewname) to a function of an unknown context?</p> <p>[*] The truth is a little bit more complicate. In zope, an object or its class can be marked at runtime with arbitrary <code>interfaces</code> (python doesn't have the concept of interfaces, this is a construction entirely of zope). The interfaces form a tree. You associate a view with a pair (interface, name). When you ask for the view of (context, name) it returns the view associated with the most specific interface. The idea is that you could change the view, changing the registry of interfaces and not modifying code.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload