• entries
359
237
• views
188610

# Tangent: Measurement intro

137 views

First off, thanks to Daerax for the great link that helped this post get along.

Secondly, w00t! I got my christmas present

And work is finally calming down after release and the clean-up of a monumental clusterfuck caused by a single mis-labeled constant used once in code (not mine). Which allowed me a little time tonight (and more time going forward) to fix Tangent bugs and bemuse myself.

A note about syntax and a minor bugfix:

public void foo( arg){    //...}

now works. foo takes one argument which must be at least a string, and stores the type into generic T. If string is omitted from the above declaration, foo will take any type.

Tonight though I started into quickie work on units of measurement for Tangent. I talked about this a little a few posts back, and Daerax's link helped remind me of schooling in the dusty parts of my mind. I'm not looking to use them so much for calculation, but more to disambiguate int into '# of _'.

I'm thinking that a little bit of this operation should be pretty easy (famous last words, I know...). Tangent already knows the concept of Type Expressions. Adding some for the measures to represent measure multiplication, division, and exponentiation should be easy. Tangent also knows fairly arbitrary overloads for types. Adding something to int/float/etc. that takes a measure and returns (int & measure) should also be pretty simple.

By the weekend, I'd like to get simple measures done. x meters is different than x feet. Then complex measures. Then measures in syntax. Then conversions. Then probably some cleanup/finishing (perhaps inferring/constraining on exponents).

So, the feature in a nutshell:

- A measure is a distinct type element; similar to an enum that it's not a record of data but instead a differentiator for a single element of data. They'll be declared similarly to a class, but use a different keyword.
- Their instances will be value types with the number as the only data.
- Extra methods and static data may be declared on the measure.
- Likely need a special conversion syntax to ensure reflexiveness of conversions.
- Likely need aliases to make meter, meters, m, metre... work.
- All measures will act as goose types.
- Measures will probably be constructable only via a # + measure type -> measure instance style. (10 seconds; 9.88 m/s^2; 1000000 usd;)
- Will probably need some caching/intelligence to make sure that a constructed type uses a declared type if it exists (eg. if you define methods for a newton and then via operations construct a (kg m/s^2) [a newton], those methods should be available)
- Likely will allow a generic parameter to define the numeric that represents the value, stealing the conversions from the numeric types (int/int -> float; int meters/int seconds -> float m/s). Too bad I'm not doing this from scratch and can't just use arbitrary precision numbers as default.

More to come as it's implemented.

Hey nice present! I have that book and its awesome. It was my primary reference* for when I was making my language way back when. Careful it does not swing/pollute you too close to the Functional Way though as the main language is OCaml and the whole thing is very theory based with lots of focus on functional + type theoretic concepts and very sparse syntaxes :p

In addition to the free book The Implementation of Functional Programming Languages and Elements of Functional Programming

But I have a few questions (sorry if they are off topic from your post). First, do you have any documentation for the language because I am really interested in some of these features you mention. One of which allowed you to do something like Ogre1 Smash Knight1. Really cool concept. Did you ever get it worked in?

public void print(Pirate target){
Console.WriteLine(target.Name);
}

The above looks like its typical code. Yet in the same example you did: print bleh;
Since when can a function be called like that? Was that a typo? Or can any 1 argument function be called like that? Or maybe any function can be called in that style separated by spaces and commas while omitting the ()?

Quote:
 Original post by Falling Sky Hey im glad your still progressing with Tangent. At one point I had thought you quit!

No, I just had a release at work that took a lot of my coding energies.

Quote:
 But I have a few questions (sorry if they are off topic from your post).

No problem at all. I love questions. Plus it provides motivation for work (and feedback about features) and provides traffic to the blog.

Quote:
 First, do you have any documentation for the language because I am really interested in some of these features you mention.

http://www.assembla.com/wiki/show/tangent-lang

The wiki has some documentation. Combined with the example code you should be able to get a feel for things. The IDE's error messages are still terrible.

Quote:
 One of which allowed you to do something like Ogre1 Smash Knight1. Really cool concept. Did you ever get it worked in?

Yes, arbitrary infix operators are in. In version 2.5, phrases are included which can allow similar things more arbitrarily:

public void    smash (GameObject Target) with (Weapon SmashingWeapon){
Target.HP = Target.HP - SmashingWeapon.Damage;
}

// ...
smash Knight with Boulder;


Quote:
 Yet in the same example you did: print bleh; Since when can a function be called like that? Was that a typo? Or can any 1 argument function be called like that? Or maybe any function can be called in that style separated by spaces and commas while omitting the ()?

The docs should have more info for this. Statements and the following screen about Operation Inference in particular. In summary:

Tangent's parser does not work like traditional parsers. With normal parsers, you get your abstract syntax tree right from the parser. * binds more strongly than + so order of operations fall out of it. Tangent's parser just spits out a stream of identifiers/operators. A post-parse step then builds the AST based on type information. All statements must result in void. That post-parse uses the type information associated with the tokens to determine what arrangement results in void (and errors if it can't).

So for print bleh; it looks at print; sees a method that takes a string and returns void (print actually has 2 methods; one returns void, one returns print again). It sees a string to the right so consumes it, making a token invoke print(bleh) -> void. Since there is now one element in the stream and it is equivalent to void, the algorithm terminates with success. Parens thus are solely there to do explicit preference in order of operations. Still, it's good form to use them when you've got parameters and commas or if the method is more like code and less like a sentence.

In general, the algorithm goes from left to right. It looks if the token is an infix operator, then a unary operator/method (all methods are unary, foo(2,3) takes an int,int tuple) . If found (and the parameters are right) it reduces the list of tokens into a new list and recurses. If success (it can reduce the list to 1 term equivalent to void), it returns. If failure, it continues along trying other permutations. If it ends, it returns failure.

## Create an account

Register a new account