chicken-users
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Chicken-users] More thoughts on Ersatz


From: Ivan Raikov
Subject: Re: [Chicken-users] More thoughts on Ersatz
Date: Thu, 14 Mar 2013 09:39:33 +0900

Hi Matt,

     Thanks for the detailed usage and bug reports and sharing your thoughts with us. Let me try to briefly respond:

1)  {% endblock <block_name> %}: the current grammar does not accept identifiers after endblock. Is this something required by Jinja?

2)  super()  function: unfortunately, the extends statement is implemented by appending template statements together. ersatz currently has no notion of classes, let alone parent and children classes. 

3) {% if not loop.last %}: this should work.  Can you check whether "if loop.last" works?

4)  Filters: sorry about the documentation, I will try to document filters soon then.

5) Tvalues: I am an SML/Ocaml groupie and fanatical believer in types :-D But if it will help, I will add a sexpr->tvalue and tvalue->sexpr procedures that can convert between Scheme values and template values.

6)  Yes, you can build templates "in-memory". Take a look at the template-statement data type. I will add this to the documentation as well.

7) Name: 'tori' is a bit too generic, I think. Presumably German is the native language of Chicken, so I chose what I think expresses the nature of templates, yet is somewhat self-deprecating, as ersatz usually means 'poor substitute' in English and my native Bulgarian.


Thanks again for your very useful comments and thoughts, Matt. Best,

  -Ivan





On Wed, Mar 13, 2013 at 1:32 AM, Matt Gushee <address@hidden> wrote:
Hello, Ivan and anyone else who is interested--

Having just finished successfully converting a web site from Python (Flask framework) to Chicken Scheme, I'd like to summarize my observations on the Ersatz template library. And let me say, Ivan, that although I have a few criticisms, I very much appreciate the work you have done to make Ersatz available. Before I discovered it, I was thinking about developing a Jinja-like template library myself, but I doubt that I could have done it as well as you have. And overall I find that Ersatz works very well and is not too difficult to use.


UNIMPLEMENTED JINJA2 FEATURES

So, first of all, I started out with a set of Jinja2 templates. I was afraid I would have to make major changes to use them with Ersatz, but it turned out I didn't need to change very much. However, I did notice a few Jinja features that are not implemented in Ersatz:

1) {% endblock <block_name> %} causes a parser error. This is certainly the least important of the issues I found.

2) The super() function is not supported. I was able to work around that limitation but splitting up some blocks. Still, this would be a very convenient feature to have.

3) {% if not loop.last %} also raises a parser error. This was a fairly important feature for me, because I have a couple of templates that generate JSON from arbitrary-length lists of objects, and there needs to be a comma *between* the objects in JSON. So I was doing this:
 
   {% for item in items %}
       {
          ....
       }
   {% endfor %}{% if not loop.last %}, {% endif %}

My solution for Ersatz was to split the list in Scheme code into all-but-last and last components, then do the following in my template:

   {% for item in items %}
      {
          "a": {{ item.a }}
          "b": {{ item.b }}
          "c": {{ item.c }}
       },
   {% endfor %}
   {
          "a": {{ last_item.a }}
          "b": {{ last_item.b }}
          "c": {{ last_item.c }}
    }

And actually, I have since realized a couple of things about this. One is that I could split the list into car & cdr, and put a 'first_item' before the 'for' block, with a comma in between. The other is that I need to ensure that 'items' is not an empty list, otherwise there will be an extra comma. Is this the way to do that?

   Scheme:
      models: `((items . ,(Tbool #f)) ...)

   Template:
      {% if items %}
         {% for item in items %}
            ,
            {
               ...
             }
          {% endfor %}
       {% endif %}


FILTERS

My application uses several custom filters. I eventually figured out how to do this, but it was rather challenging. First of all, the documentation does not cover how to program a filter. For the record, I found that both the inputs and outputs of user-defined filters need to be tvalues. I also found that when I tried to use a filter that accepted one argument, Ersatz was passing 2 arguments to it. I have no idea what the second argument is supposed to be; I added some code to pretty-print the second argument to a log file, and found that it was an empty list.

I'm wondering if you [Ivan] have any recommendations on the best way to write user-defined filters. I can't use the 'func1-arg', 'func2-arg', etc. procedures that you use for the built-in filters, since those procedures are not exported. And in any case, I haven't read the code thoroughly enough to really understand what they are doing.

I also found an error in the documentation related to filters. The signature of the 'template-std-env' function says that the filters: keyword argument should be a string list. In fact, it is a tvalue-alist of the same form as the models: argument to 'from-file' and 'from-string'.


TVALUES

Now we come to a philosophical issue. To put it bluntly, I feel that the tvalue datatype makes using Ersatz more complicated than it needs to be without providing any huge benefit. By the way, I spent several years programming in OCaml, so I know where this comes from. And, though in general I liked the language and the community, and found the OCaml type system very useful for certain kinds of applications, I was never convinced that type safety was a panacea, and in particular I don't see it as very useful for general web programming. Because in my limited experience, most web applications don't do much complex data manipulation. Most of the complexity is in (a) rendering pages; (b) validating user input; and (c) database storage, search, and retrieval. The kinds of data structures that an OCaml-ish type system helps with tend to be very short-lived. To give one of many possible examples, let's say your application accepts registrations of new users. Okay, so the user enters a username and password and some other information in a form. If the form input is accepted, then probably your app just puts it straight into the database, reports to the user that their registration was successful, and moves on to the next task. Now there are certain validation tasks that need to be done before accepting the registration. For example, you need to ensure that the username is unique. Well, that's just a matter of matching a string input against existing strings in the database. Et cetera ... in any case, most or all of the data in this type of interaction consists of strings; and while they often need to be validated, that validation usually needs to be done according to semantic constraints that are difficult or impossible to describe in even a very sophisticated type system. I could go on, but hopefully my point is clear, whether you agree with it or not.

As for what I think you should do, well, probably you should gather more feedback on the merits of the tvalue datatype before considering any changes. Maybe there are common use cases where this kind of type-safety is really helpful, and maybe it makes the library easier to maintain. And I plan to continue using Ersatz anyway, I just feel that the need to wrap everything in tvalues makes it harder than it needs to be.


(NOT HAVING) IN-MEMORY TEMPLATE OBJECTS?

I have worked with several template systems in the past, and it seems like most of them allow/require you to instantiate a template object in memory, to which you can then feed variables and other contextual parameters before rendering the output. Although this adds a bit of complexity to the user's code, it seems like a good idea to me, because you can then reuse a template by resetting it and passing it new variables. Though I have no idea how much this affects performance vs. reading and parsing the template from a string or file on each request, it surely makes some difference. Though I have noticed there are several procedures for working with template statements ... perhaps that addresses this usage in some way? I don't really understand how that works yet.


THE NAME OF THE EGG

Okay, this is not entirely serious, but ... since, as I said, I was thinking about creating a Jinja-like template library myself: the name Ersatz doesn't make sense to me. I would like to call the egg Torii (heh-heh). I've noticed that you [Ivan] are in Japan, so I am assuming you know at least a little Japanese ... so maybe you can understand my little pun there.

Anyway, thanks for everything, and goodbye for now.

--
Matt Gushee

_______________________________________________
Chicken-users mailing list
address@hidden
https://lists.nongnu.org/mailman/listinfo/chicken-users



reply via email to

[Prev in Thread] Current Thread [Next in Thread]