Live data from Hacker News

Why are we templating YAML?

leebriggs.co.uk

341–350 of 351 posts

Re: Why are we templating YAML?

#342
post #341

Earlier quoted context omitted.

Lack of validation?

JSON files don't have validation support like XML schema does.

Maybe not built in like DTD, closer to XML Schema in its laid-on-top manner, but I would argue that JSON Schema is fairly good, and on its way to becoming an IETF standard: http://json-schema.org/ and https://tools.ietf.org/html/draft-handrews-json-schema-01 (et al)

The lack of obvious namespace management is suboptimal, but so far I thankfully haven't encountered a situation where it was a show-stopper.

IntelliJ and related tools also allow associating a json schema with a YAML file, which I have found infinitely handy

Re: Why are we templating YAML?

#343

Earlier quoted context omitted.

This is how Lua started, as a config language, but it gradually added more features that people found useful in config, and became Turing complete.

Lua was TC from the start, it came with the procedural concepts from Modula - if/while/repeat - and functions.

I meant SOL, the predecessor, before Lua proper.

Re: Why are we templating YAML?

#344

Earlier quoted context omitted.

Most programming editors rearrange the white space of the files they open. Some do it more, some do it less. Rearranging white space in a YAML file often destroys information.

Mine do no such thing. The only whitespace that gets stripped in /any/ editors I have are trailing whitespace and extra whitespace before the EOF, and that's only in certain IDEs where I have consciously enabled these options. They are disabled by default. Removing trailing whitespace should never change the logic of a file in general, but as for YAML it certainly doesn't. And editors should never remove leading whit…

Press tab on a line in emacs, and the whitespace will get rearranged. It's more explicit in vi, but don't bother (un)indenting blocks there either.

Just writing characters anywhere a file on the MS IDEs I've tried is enough to rearrange the line's whitespace, while the Jetbrain's I've tried are more conservative and won't break lines you haven't changed somehow.

Re: Why are we templating YAML?

#345

Earlier quoted context omitted.

Mine do no such thing. The only whitespace that gets stripped in /any/ editors I have are trailing whitespace and extra whitespace before the EOF, and that's only in certain IDEs where I have consciously enabled these options. They are disabled by default. Removing trailing whitespace should never change the logic of a file in general, but as for YAML it certainly doesn't. And editors should never remove leading whit…

Press tab on a line in emacs, and the whitespace will get rearranged. It's more explicit in vi, but don't bother (un)indenting blocks there either. Just writing characters anywhere a file on the MS IDEs I've tried is enough to rearrange the line's whitespace, while the Jetbrain's I've tried are more conservative and won't break lines you haven't changed somehow.

Ok, now show me a single editor that doesn't make whitespace changes when you press tab.

I've only ever had issues with vim messing up whitespace on the line I'm typing specifically with regard to YAML, and yes that's an issue but it has nothing to do with YAML. For example, Adding another colon to a string, wrapped in whitespace or not, will often reduce indentation. That's just plain bad behavior, but it's not intended behavior.

Re: Why are we templating YAML?

#346

I didn't see this mentioned anywhere else, so another alternative (that I've seen and really like conceptually, but haven't used so far) to all this wildness with YAML and JSON -> https://github.com/dhall-lang/dhall-lang , and for kubernetes specifically -> https://github.com/dhall-lang/dhall-kubernetes

For the sake of this comment, let's define "templating" to be attempts to solve the problem "I need $FORMAT due to an existing constraint, but $FORMAT does not entirely meet my needs on its own" (in this article, $FORMAT is YAML). Additionally let's say that in order to be a "template" something must be a text file (e.g. exporting a database table as $FORMAT does not count as "templating" for the purposes of this comment).

I think there are three very different kinds of tools that people use for this:

1. Interpolation/preprocessor languages: This is what the author is talking about. There are delimiters/tags/sigils to distinguish "the templated parts" from "the rest" and the primary operation done by the template engine is substitution. "The rest" is literal content that's already in $FORMAT and it remains mostly/entirely unchanged during template rendering. Languages of this type are basically glorified `sed`. This can be nice because they're agnostic as to their embedding (any string will do) so they're very portable/flexible (you don't have to create "handlebars for YAML", "handlebars for HTML", "handlebars for CSV", etc; one implementation does it all). Languages of this kind can work in the small but don't scale well for all the reasons mentioned in the article/comments. The language doesn't know anything about the semantics of $FORMAT and that can cause all kinds of pain. Examples include golang templates, PHP, ERB, handlebars, the C preprocessor, Jinja, etc.

2. Compilers/code generators: These are "complete" languages that compile to $FORMAT. The difference between these and and interpolation/preprocessor languages is that the entire input is the language, not just specific chunks/tags. This kind of language can be nice because you have complete control and can therefore guarantee valid output and do tricks like supporting multiple different output formats for the same input, but the downside is that you're working with an entirely new language so there's a learning curve, you need specialized syntax highlighters and other tools to work with templates, etc. Examples include HAML, Jsonnet, Dhall, etc.

3. Embedded DSLs: Templates of this kind are valid $FORMAT from the beginning, but have embedded ways to specify transformations to be applied to the parsed AST. These languages are homoiconic with respect to $FORMAT. First $FORMAT is parsed, then the template engine iterates through the AST to perform evaluations, then the result can either be used as-is in memory or serialized back to (a possibly different) $FORMAT. This is sort of like an interpolation/preprocessor language with the evaluation order swapped: preprocessing is "run the template engine, then parse $FORMAT" while this is "parse $FORMAT, then run the template engine". A downside of this approach is that it is less general, e.g. it only really makes sense when $FORMAT has a well-defined structure (you probably can't template plain english sentences with this approach), but these days most "data languages" have converged towards being semantically equivalent to JSON (lists, dictionaries, and primitives) and this approach works well for any of them. An upside is that like compilers/code generators you can guarantee that the output will be valid $FORMAT no matter what the template looks like. Examples include JSON-e, Lisp macros, CloudFormation templates, etc.

It's unfortunate that all of these get called "templating languages" because they're very different beasts from one another, and usually when I see conversations about this stuff these distinctions get blurred and you end up with apples-vs-oranges comparisons. If I had my druthers we'd reserve the word "templating" for the first one and use different terminology for the others, but that ship has sailed.

Re: Why are we templating YAML?

#347

I know I'm in a minority, but I really dislike YAML... I recently did a lot of Ansible and boy, at the beginning, I was just struggling a lot. Syntactic whitespace kills me. I don't like it in Python either, but for some reason, when I write Python, it's a lot easier. Maybe YAML is just a bit more complex (and Python has better IDE support..?)

I don't mind YAML. I dislike that something things become strings and sometimes they become other types: foo: bar # {"foo":"bar"} foo: "bar" # {"foo":"bar"} foo: 42 # {"foo":42} foo: "42" # {"foo":"42"} Other than that, no major complaints. My editor understands YAML and shows the indentation level in the background (highlight-indentation-mode) and auto-formats files so they all have consistent indentation (prettier-…

How about...

    foo: yes  # "foo": true
    bar: YEs  # "bar": "YEs"
    baz: YES  # "baz": true

Re: Why are we templating YAML?

#348

Earlier quoted context omitted.

There's absolutely no way that this (copied from https://github.com/akeeba/fof/wiki/The-XML-configuration-fil... ): items ABCD123456 HTTPBasicAuth_TOTP,QueryString_TOTP published foo,bar,baz browse core.manage 3 is at all preferable to this: (fof (common (container (component-namespace "MyCompany\\MyApplication")) (dispatcher (default-view items)) (authentication (totp-key ABCD123456) (authentication-methods (http-ba…

You changed the model when you adapted the xml to lisp. You decided that some tags are unnecessary, dropped some attributes and assumed others are merely different types of child nodes - and now your sample doesn't actually have the same semantic meaning as the XML example. You also removed some comments. Was all this done to emphasis how much cleaner a lisp alternative would be? If we're playing this game, you can a…

> You changed the model when you adapted the xml to lisp.

That was a conscious decision, because the verbosity of XML prevents clear understanding of a data model, while the cleanness of S-expressions enables a clarity of vision which enables prudent judgement when laying out a data structure.

> You also removed some comments.

Yes, because they were akin to:

    // Add 1 & 2, assign to X
    x = 1 + 2
If you really want an S-expression version of the XML in that example, here is SXML[0]:

    (*top*
     (fof
      (*comment* " Common settings ")
      (common
       (*comment* " Container configuration ")
       (container
        (option (@ (name "componentNamespace")) "MyCompany\\MyApplication"))
    
       (*comment* " Dispatcher configuration ")
       (dispatcher
        (option (@ (name "defaultView")) "items"))
    
       (*comment* " Transparent authentication configuration ")
       (authentication
        (option (@ (name "totpKey")) "ABCD123456")
        (option (@ (name "authenticationMethods"))
                "HTTPBasicAuth_TOTP,QueryString_TOTP"))
    
       (*comment* " Model configuration. One tag for each Model. ")
       (model (@ (name "orders"))
              (*comment* " Model configuration ")
              (config
               (option (@ (name "tbl")) "#__fakeapp_orders"))
    
              (*comment* " Field aliasing. One tag per aliased field ")
              (field (@ (name "enabled")) "published")
    
              (*comment* " Relation setup. One tag per relation ")
              (relation (@ (type "hasMany") (name "items")))
              (relation
               (@ (type "belongsToMany") (name "transactions")
                  (localKey "foobar_order_id") (foreignKey "foobar_transaction_id")
                  (pivotLocalKey "foobar_order_id")
                  (pivotForeignKey "foobar_transaction_id")
                  (pivotTable "#__foobar_orders_transactions")))
              (relation
               (@ (type "belongsTo") (name "client")
                  (foreignModelClass "Users@com_fakeapp")))
    
              (*comment*
               " Behaviour setup. Use merge=\"1\" to merge with already defined behaviours. ")
              (behaviors (@ (merge "1")) "foo,bar,baz"))
    
       (*comment* " Controller, View and Toolbar setup. One tag per view. ")
       (view (@ (name "item"))
             (*comment* " Controller task aliasing ")
             (taskmap
              (task (@ (name "list")) "browse"))
    
             (*comment* " Controller ACL mapping ")
             (acl
              (task (@ (name "dosomething")))
              (task (@ (name "somethingelse")) "core.manage"))
    
             (*comment* " Controller and View options ")
             (config
              (option (@ (name "autoRouting")) "3"))
    
             (*comment* " Toolbar configuration ")
             (toolbar (@ (title "COM_FOOBAR_TOOLBAR_ITEM") (task "edit"))
                      (button (@ (type "save")))
                      (button (@ (type "saveclose")))
                      (button (@ (type "savenew")))
                      (button (@ (type "cancel"))))))
    
      (*comment* " Component backend options ")
      (backend
       (*comment* " The same options as Common Settings apply here, too "))
    
      (*comment* " Component frontend options ")
      (frontend
       (*comment* " The same options as Common Settings apply here, too "))))
Which I think is still indubitably and inarguably clearer & cleaner than the XML version.

Technically, the XML spec requires whitespace preservation, so really it’s this:

    (*top*
     (fof "
        "
          (*comment* " Common settings ") "
        "
          (common "
            "
                  (*comment* " Container configuration ") "
            "
                  (container "
                "
                             (option (@ (name "componentNamespace")) "MyCompany\\MyApplication") "
            ")
                  "
    
            "
                  (*comment* " Dispatcher configuration ") "
            "
                  (dispatcher "
                "
                              (option (@ (name "defaultView")) "items") "
            ")
                  "
            
            "
                  (*comment* " Transparent authentication configuration ") "
            "
                  (authentication "
                "
                                  (option (@ (name "totpKey")) "ABCD123456") "
                "
                                  (option (@ (name "authenticationMethods"))
                                          "HTTPBasicAuth_TOTP,QueryString_TOTP")
                                  "
            ")
                  "
    
            "
                  (*comment* " Model configuration. One tag for each Model. ") "
            "
                  (model (@ (name "orders")) "
                "
                         (*comment* " Model configuration ") "
                "
                         (config "
                    "
                                 (option (@ (name "tbl")) "#__fakeapp_orders") "
                ")
                         "
            
                "
                         (*comment* " Field aliasing. One tag per aliased field ") "
                "
                         (field (@ (name "enabled")) "published") "
    
                "
                         (*comment* " Relation setup. One tag per relation ") "
                "
                         (relation (@ (type "hasMany") (name "items"))) "
                "
                         (relation
                          (@ (type "belongsToMany") (name "transactions")
                             (localKey "foobar_order_id") (foreignKey "foobar_transaction_id")
                             (pivotLocalKey "foobar_order_id")
                             (pivotForeignKey "foobar_transaction_id")
                             (pivotTable "#__foobar_orders_transactions")))
                         "
                "
                         (relation
                          (@ (type "belongsTo") (name "client")
                             (foreignModelClass "Users@com_fakeapp")))
                         "
    
                "
                         (*comment*
                          " Behaviour setup. Use merge=\"1\" to merge with already defined behaviours. ")
                         "
                "
                         (behaviors (@ (merge "1")) "foo,bar,baz") "
            ")
                  "
    
            "
                  (*comment* " Controller, View and Toolbar setup. One tag per view. ") "
            "
                  (view (@ (name "item")) "
                "
                        (*comment* " Controller task aliasing ") "
                "
                        (taskmap "
                    "
                                 (task (@ (name "list")) "browse") "
                ")
                        "
                
                "
                        (*comment* " Controller ACL mapping ") "
                "
                        (acl "
                    "
                             (task (@ (name "dosomething"))) "
                    "
                             (task (@ (name "somethingelse")) "core.manage") "
                ")
                        "
                
                "
                        (*comment* " Controller and View options ") "
                "
                        (config "
                    "
                                (option (@ (name "autoRouting")) "3") "
                ")
                        "
                
                "
                        (*comment* " Toolbar configuration ") "
                "
                        (toolbar (@ (title "COM_FOOBAR_TOOLBAR_ITEM") (task "edit")) "
                    "
                                 (button (@ (type "save"))) "
                    "
                                 (button (@ (type "saveclose"))) "
                    "
                                 (button (@ (type "savenew"))) "
                    "
                                 (button (@ (type "cancel"))) "
                ")
                        "
            ")
                  "
        ")
          "
        
        "
          (*comment* " Component backend options ") "
        "
          (backend "
            "
                   (*comment* " The same options as Common Settings apply here, too ") "
        ")
          "
        
        "
          (*comment* " Component frontend options ") "
        "
          (frontend "
            "
                    (*comment* " The same options as Common Settings apply here, too ") "
        ")
          "
    "))
But I think that rather proves my point: XML obscures that which should be obvious.

(and apologies for these terribly vertical posts — I think that they go a long way towards demonstrating the need for a compact information representation).

0: http://okmij.org/ftp/Scheme/xml.html#SXML-spec

Re: Why are we templating YAML?

#349

Earlier quoted context omitted.

I don't mind YAML. I dislike that something things become strings and sometimes they become other types: foo: bar # {"foo":"bar"} foo: "bar" # {"foo":"bar"} foo: 42 # {"foo":42} foo: "42" # {"foo":"42"} Other than that, no major complaints. My editor understands YAML and shows the indentation level in the background (highlight-indentation-mode) and auto-formats files so they all have consistent indentation (prettier-…

How about... foo: yes # "foo": true bar: YEs # "bar": "YEs" baz: YES # "baz": true

Yeah, that's a little crazy. It's the classic case of in-band signalling. It never works. I wish quotes around strings were mandatory, then having 83 ways to say "true" would be OK. But when strings randomly get upgraded to other primitive types... it's a little weird.
Post reply on HN