vJass使用者进阶到Wurst

Last updated: August 21, 2017
Author: Cokemonkey11

Wurst for vJass Users

  阅读该指南的时候最好熟悉vjass

  这个教程是某种非正式讨论的形式,但交流中很多关键点由易于理解的高级编程范式所支持.

(译者:这篇是给用vjass的人安拉wurst的) —— 当我们讨论wurst的时候,我听到一些奇怪的反对使用它的说法. 有些说法也蛮有意思的,比如”我就是没办法适应,学习它”,”我的图已经在用vjass嘞”,但是其他的讨论还是蛮奇怪的,值得一些指导. 更精准的说,有许多关于wurst的讨论充斥着误解和谣言. Wurst,是一门简单易学的语言,并且你可以边用vJass边用wurst。

作为背景:我在几年前选择改用wurst,在这之前我已经有了充分的编写vjass代码的经验.发生这个改变也很简单.我仅仅是想试着用wurst做个自娱自乐的小图,但立刻意识到我之前被公众舆论所带偏和误导了. 不仅仅是我,绝大部分的开发者都将它视作了某种vjass或者zinc的高级变种. 所以这篇文章的目的不仅仅是学习,也是正名.

为什么不用Vjass呢

wurst使用者关于这方面的讨论并不出众 vjass最有趣的问题不是蠢到爆炸的怪异语法比如”extends array”或者宏定义系统.这些小臭毛病指向了一个更大的问题.这个语言是个畸形的缝合怪,把一大堆事物缝合在了一起. 更有意思的讨论vjass不是个好选择的探讨在下方.

Preprocessor

Fundamentally, vJass is a pre-processed language that gets very naively transformed into Jass, which has lots of side-effects:

  Key point: Type-Safety is the concept of checking that the values and functions a programmer uses are consistent - e.g. that the user never tries to give a timer to a function that takes an integer.

  • vJass Generates code for you, but otherwise is very limited to abstraction - you just can’t express higher level concepts safely in vJass because instances of structs are always integers - the compiler can’t check anything for you - so type-safety is lost entirely. vJass does at least have some validation, but that’s not being done by JassHelper - PJass is checking the emitted jass, but can’t do anything smart at the vJass level.
  • No “abstract syntax tree” (building blocks of code that a compiler understands) - so adding new features to JassHelper is very expensive and requires detailed knowledge of the system, which makes it hard for anyone else to take up ownership.
  • Compiler-aided optimisation is impossible. Wurst can inline functions, cull unused code, automatically null variables, etc. JassHelper can inline 1-line functions, but this is pushing the boundaries of its capabilities.

Limited tooling

JNGP/WEX are excellent tools because they attempt to centralise tooling into an integrated experience.

Wurst also centralises tooling - to your code editor, intead of to your world editor. This adds boundless value because code editors are designed from scratch to be extensible, whereas our world editors have to hack the blizzard interface using Grimoire.

  • You can work on your map without wc3 or any particular OS - and you still get compiler feedback.
  • Code highlighting, autocomplete, Jump-to-declaration for all code - these are features that TESH can’t do.
  • Code gets parsed live - the feedback loop for warnings and errors is in real time.

It can feel unapproachable to be launching a map magicaly using a code editor, but it becomes second nature very fast.

Fragmented

There’s more than one JassHelper, and they’re hardly different. Indeed, some very ubiquitous vJass libraries don’t even work with the original JassHelper specification!

Typeless

Because vjass can only expand your code into Jass, it can’t make any guarantees about the type of values. This leads to tricky attaching and detaching of struct intances to timers and the like.

Indeed, it’s possible to cast instances of objects to integer in Wurst too - but it’s not a recommended technique and problems should instead be solved via dynamic dispatch or closures!

Wurst, like this, grants you the capability to prevent entire classes of errors.

No Scope for Improvement

Maintaining JassHelper is hard, and the language specification designer isn’t active anymore.

Contrast that with the 33 commits so far this month on WurstScript.

Of course, there are costs to using a language that’s actively developed too - strange bugs are more numerous, and occasionally the compiler has a regression - but this is a small price to pay.

Why wurst?

Integrated developer experience with code first

As discussed above, wurst also provides a sleek development experience - but unlike vJass, it puts the code first. Using object editing, compiletime functionality and the vast standard library, all objects can be represented in code and be versioned using git.

A true compiler

Wurst is a compiled language, which fundamentally gives it advantages and scope for enormous, leaping improvements, like:

  • Ipdates that improve performance, by way of optimiser changes
  • Debug information for failure scenarios - including in-game stack traces
  • Automatic nulling of variables to prevent tedious leaks

Higher levels of abstraction

The basic wurst language implemented is a higher level language that lets you express more while writing less.

Have you ever had an experience with vJass where you slammed out a hundred lines that implemented a stack or linked list, and it all felt very natural and rewarding?

You’ll have a similar experience when you write just a few lines of wurst and get the same functionality - these experiences are mind bending and highly valuable in the context of getting work done.

Standard Libary

The convention in vJass is to share versioned libraries online as code snippets, but not in wurst. Why is that?

It’s not because wurst isn’t worth sharing, or there’s noone sharing it - wurst just fundamentally approaches the problem at a tooling level (much like cargo, pip, npm, etc).

Most of the basic requirements like data structures and familiar systems like damage detection are implemented in the standard library. import DamageType and done. Don’t like the implementation? Implement your own and publish it for use with the wurst dependency manager, offer your own improvements upstream to the standard library, or even write your own standard library - why not?

We already received many very valuable additions to the stdnard library. By peer review and contributions it ensures a flawless beginner experience, without needing to worry about dependencies before starting a map.

Object editing

That cool LUA thing where object data gets accessed but kind of doesn’t work these days?

Yeah, wurst has that on steroids. Wurst has a feature called compiletime functions, where functions can be declared compiletime, and thence will be excuted when wurst runs.

Compiletime functions have a slightly different set of features than runtime functions, but this includes a convenience library for object editing. Let’s have the code speak for itself -

public let DRAGON_ID = 'h000'

class DragonDefinition extends UnitDefinition
    construct(int id)
        super(id, 'nadr')

        setAttacksEnabled(0)
        setName("Dragon")
        setUpgradesUsed("")
        setStructuresBuilt("")
        setManaRegeneration(1.)
        setManaInitialAmount(0)
        setManaMaximum(400)
        setFoodCost(1)
        setSpeedBase(300)
        setTooltipBasic("Dragon")
        setTooltipExtended("An evolving beast with special powers.")
        setNormalAbilities("")
        setRace(Race.Human)
        setScalingValue(1.)

@compiletime function dragon()
    new DragionDefinition(DRAGON_ID)

On performance

This subject is scary complicated, but it’s something people often bring up when discussing vJass and indeed wurst.

Let’s get some basics down:

  • Coders interested in performance tend to appreciate that in vJass, the shape of the emitted jass tends to be predictable and close to to the low level function designed. This lets them reason about performance.
  • Higher level languages like C++ use an optimizer to improve performance by doing things like automatically unraveling loops, inlining functions, and throwing away unused data or code paths.
  • In the past, higher level languages that still provide system-level interaction tended to dominate this area (c++), although advances in compiler technologies has led to languages like rust and go, that manage to show how a programming language can very well provide a high level of abstraction while also being blazing fast.

  Key point: Performance and Level of Abstraction are orthogonal concepts - and writing higher level code does not necessarily make it slower.

Standards:

In vJass, the standard way to write code is the maximally low-level and/or accepted way to implement some function. This leads to things like “use array extends when possible” and “prefer unit indexer”.

In wurst, the standard way to write code is the maximally maintainable way to implement that function. This encompasses things like:

  • readability
  • documentation
  • comfortable level of abstraction and formatting

This is actually harder, because the design level thinking is less obvious, and the coder is being pulled in many directions instead of just the standard route. The reason this is better is because any level of readability improvement, documentation, or comfort is better than none/flat (as in the case of vJass).

  Key point: Thinking about performance in wurst, and indeed many high level programming languages, is a pitfall, and for newcomers this may feel unfamiliar - you have to actively change your behavior to get the most out of wurst.

  Key point: Let the compiler/optimiser do the work for you. Instead, write the code that feels right.

Is vJass bad then?

For many reasons vJass is still better than plain jass, but JassHelper is inherently a fairly basic preprocessor, so you can’t have expectations about it being good enough, and ought not to settle for it being the best, or the standard.

More to the point: there was a time when vJass was absolutely the best option for mapping in wc3 - let this be a lesson in economics about being first to market :)

Is wurst good

Yes. Wurst is more in line with modern, widely accepted programming language design goals. Wurst provides higher levels of abstraction that let you write more expressive code in less time, without sacrificing performance. Wurst’s status as an actively developed, compiled language means that there is scope to improve it in any of those orthogonal contexts.

Wurst code can break, and things change with it from month to month. Wurst can have bugs. But on average, the value of using wurst just vastly outweighs those negatives - and indeed, most arguments I hear about wurst have nothing to do with these issues.

But what about cJass, Zinc, vrJass?

No jass compiler that I’ve seen comes close to the capabilities of wurst, and many of these are just toy projects. Looking at the state of those projects actually just detracts from understanding the value of wurst.

Summary

Wurst lets you write better code, faster.

Wurst cuts out some of the warts of vJass in favor of more standard, type-safe ways of approaching the same problem. For example, there is no macro system - instead, generics let you write types that are modular.

Furthermore, it’s a goal of the wurst community to be explicitly about productivity - all this evangelism about language design can only go so far, but the killer argument is how much efficiency can be derived from using wurst, and that starts with the standard library.

FAQs

  Wurst seems cool and worth checking out, but I can’t get past the syntax. Why does it look like python? I prefer the style of [c, Java, ECMA]

I get this question a lot, but it comes at a weird angle and surprises me. Let’s talk about four different things related to this:

The value of syntax

The syntax of a programming language does have implications - what’s possible, what’s convenient, aesthetics, etc, and indeed wurst’s syntax style affects how we write wurst, but I tend to get this question when people aren’t actually thinking about these properties.

Instead, the question is about how style affects reading and writing, at a behavioral level. Programmers who use one language exclusively tend to develop strong expectations for how fast or slow they can read code, and what sort of flow their understanding takes as they traverse a file.

Indeed, this is different for wurst for those highly familiar excluively with c or JASS. I would compel the reader to bite the bullet and dive into wurst anyway, as these behavioral reading and thinking patterns are a small factor in writing and maintaining code, and your ability to understand code will broaden as you experience other styles.

Functional and Imperative style

I’m not going to talk about the value of Functional programming here, but rather, about style. In my experience, people tend to have visited functional programming style briefly, and then unintentionally dismissing code or programming style that reminds them of that experience, even if the code/style under scrutiny is vastly different. More concisely, they judge a book by its cover.

How much some programming language is imperative, declarative, functional, strongly typed, or object oriented, are highly orthogonal concepts, which people tend to struggle with. With that in mind, it would be a shame to dismiss every language based on reasons like…

  • too much or not enough imperative
  • too much or not enough declarative
  • too much or not enough functional
  • too much or not enough strongly typed
  • too much or not enough object oriented

Because you’ll leave yourself misunderstanding a very wide range of concepts. Casual users of wurst would have a hard time deciding “how strongly typed” or “how functional” the language is - so they also shouldn’t feel qualified to dismiss it in the context of one of those topics.

  Key point: Wurst provides the capability to write high-level code, but isn’t otherwise anything like python or Javascript - wurst is a strongly typed language.

One last thought: just because wurst supports some abstract concepts like generics, closures, and iterators, doesn’t mean you’re forced to use them. On the contrary, I would claim that even very basic, imperative looking wurst will have fewer bugs and run faster than similarly styled vJass.

Picking your battles

This is more fundamental. It isn’t interesting to say that wurst’s whitespace/block/mutability style is grounds for dismissal, because the arguments I have to raise to discuss this are so far beyond what’s necessary to talk about the value of wurst.

Wurst’s killer features are just more interesting to talk about, so dismissing them by talking about python style is (usually unintentionally) a worthless strawman.

Jurst

I’ve saved the best for last, because this is such a middle finger to the whitepace argument. You can write wurst with block-style formatting. It’s allowed. Just see the “jurst” section in the manual.