[ANN] fixed point math library

422 views
Skip to first unread message

robert engels

unread,
Nov 29, 2018, 1:47:37 AM11/29/18
to golang-nuts
For those interesting in financial apps, I have released ‘fixed' at https://github.com/robaho/fixed a high performance fixed-point math library primarily designed for to work with currencies.

The benchmarks: (Decimal is the shopspring library, big Int/Float are the stdlib)

BenchmarkAddFixed-8         	2000000000	         0.83 ns/op	       0 B/op	       0 allocs/op
BenchmarkAddDecimal-8       	 3000000	       457 ns/op	     400 B/op	      10 allocs/op
BenchmarkAddBigInt-8        	100000000	        19.2 ns/op	       0 B/op	       0 allocs/op
BenchmarkAddBigFloat-8      	20000000	       110 ns/op	      48 B/op	       1 allocs/op
BenchmarkMulFixed-8         	100000000	        12.4 ns/op	       0 B/op	       0 allocs/op
BenchmarkMulDecimal-8       	20000000	        94.2 ns/op	      80 B/op	       2 allocs/op
BenchmarkMulBigInt-8        	100000000	        22.0 ns/op	       0 B/op	       0 allocs/op
BenchmarkMulBigFloat-8      	30000000	        50.0 ns/op	       0 B/op	       0 allocs/op
BenchmarkDivFixed-8         	100000000	        19.3 ns/op	       0 B/op	       0 allocs/op
BenchmarkDivDecimal-8       	 1000000	      1152 ns/op	     928 B/op	      22 allocs/op
BenchmarkDivBigInt-8        	20000000	        68.4 ns/op	      48 B/op	       1 allocs/op
BenchmarkDivBigFloat-8      	10000000	       151 ns/op	      64 B/op	       2 allocs/op
BenchmarkCmpFixed-8         	2000000000	         0.28 ns/op	       0 B/op	       0 allocs/op
BenchmarkCmpDecimal-8       	100000000	        10.8 ns/op	       0 B/op	       0 allocs/op
BenchmarkCmpBigInt-8        	200000000	         8.37 ns/op	       0 B/op	       0 allocs/op
BenchmarkCmpBigFloat-8      	200000000	         7.74 ns/op	       0 B/op	       0 allocs/op
BenchmarkStringFixed-8      	20000000	        99.0 ns/op	      16 B/op	       1 allocs/op
BenchmarkStringDecimal-8    	 5000000	       326 ns/op	     144 B/op	       5 allocs/op
BenchmarkStringBigInt-8     	10000000	       209 ns/op	      80 B/op	       3 allocs/op
BenchmarkStringBigFloat-8   	 3000000	       571 ns/op	     272 B/op	       8 allocs/op

Jan Mercl

unread,
Nov 29, 2018, 5:21:05 AM11/29/18
to robert engels, golang-nuts
On Thu, Nov 29, 2018 at 7:47 AM robert engels <ren...@ix.netcom.com> wrote:

> For those interesting in financial apps, I have released ‘fixed' at https://github.com/robaho/fixed a high performance fixed-point math library primarily designed for to work with currencies.

- To me type name 'fixed.Fixed' sounds like Javaism. Go code usually tries to avoid such stutter: 'sort.Interface', 'big.Int' etc.

- A struct with a single field could be replaced by the field itself. OTOH, it would enable coding errors by applying arithmetic operators to it directly, so it's maybe justified in this case if that was the intention.

- I'd prefer a single constructor 'New(int64)' and methods 'SetString', 'SetFloat' etc.


> The benchmarks: (Decimal is the shopspring library, big Int/Float are the stdlib)

I don't consider comparing performances of 64 bit integer arithmetic and arbitrary sized arithmetic very useful.

--

-j

messju mohr

unread,
Nov 29, 2018, 5:41:41 AM11/29/18
to robert engels, golang-nuts
Hello,

this looks like a really nice and useful library! :)

Just one thing: At first glance i saw that fixed.Cmp() returns 0 when both operands are NaN.
I think it would be more consistent if fixed.Cmp() would return NaN if any of it's operands are NaN.

just my 2ct
messju


On Thu, Nov 29, 2018 at 12:47:05AM -0600, robert engels wrote:
> For those interesting in financial apps, I have released ‘fixed'
> at [1]https://github.com/robaho/fixed a high performance fixed-point math
> --
> You received this message because you are subscribed to the Google Groups
> "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to [2]golang-nuts...@googlegroups.com.
> For more options, visit [3]https://groups.google.com/d/optout.
>
> References
>
> Visible links
> 1. https://github.com/robaho/fixed
> 2. mailto:golang-nuts...@googlegroups.com
> 3. https://groups.google.com/d/optout

Robert Engels

unread,
Nov 29, 2018, 8:00:57 AM11/29/18
to Jan Mercl, golang-nuts
Thanks for the feedback. My comments below. 

- To me type name 'fixed.Fixed' sounds like Javaism. Go code usually tries to avoid such stutter: 'sort.Interface', 'big.Int' etc.

To me that’s a limitation of Go with small packages like this that only have a single public struct. It is based on decimal.Decimal so I’m not the only one who thinks this....

- A struct with a single field could be replaced by the field itself. OTOH, it would enable coding errors by applying arithmetic operators to it directly, so it's maybe justified in this case if that was the intention.

It was the intention. The Raw methods are there temporarily and will be removed for direct serialization  via a Writer. 

- I'd prefer a single constructor 'New(int64)' and methods 'SetString', 'SetFloat' etc.

Not possible. The caller doesn’t know the int64 value. Also, think of how that would look in a chained math statement. Horrible. 

> The benchmarks: (Decimal is the shopspring library, big Int/Float are the stdlib)

I don't consider comparing performances of 64 bit integer arithmetic and arbitrary sized arithmetic very useful.

Those are the alternatives to use when performing fixed place arithmetic. In fact decimal.Decimal uses big Int... so it is included for reference. 
--

-j

Robert Engels

unread,
Nov 29, 2018, 8:01:39 AM11/29/18
to messju mohr, golang-nuts
NaN cannot be returned in an int so not possible.
> To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

Robert Engels

unread,
Nov 29, 2018, 8:15:38 AM11/29/18
to Jan Mercl, golang-nuts
Also, the most important reason against the setters - a Fixed is immutable. 

Jan Mercl

unread,
Nov 29, 2018, 8:21:21 AM11/29/18
to Robert Engels, golang-nuts
On Thu, Nov 29, 2018 at 2:00 PM Robert Engels <ren...@ix.netcom.com> wrote:


>> - To me type name 'fixed.Fixed' sounds like Javaism. Go code usually tries to avoid such stutter: 'sort.Interface', 'big.Int' etc.
> To me that’s a limitation of Go with small packages like this that only have a single public struct. It is based on decimal.Decimal so I’m not the only one who thinks this....

I don't think we are talking about the same thing here. Go idiom is to name types such that they are not the same as the package qualifier (modulo case) at the caller site. So the exported type should be 'Int', or 'Float' or 'Real' or 'Number', etc., not 'FIxed' to avoid 'fixed.Fixed' at caller site. `var n fixed.Number` looks better to me, for example, than `var n fixed.Fixed`. The later actually does not even communicate any hint what the type could possibly be.

>> - A struct with a single field could be replaced by the field itself. OTOH, it would enable coding errors by applying arithmetic operators to it directly, so it's maybe justified in this case if that was the intention.
> It was the intention. The Raw methods are there temporarily and will be removed for direct serialization via a Writer. 

Then it looks strange that to construct a Fixed from int64 one has to write 'fixed.NewF(0).FromRaw(42)'. Check the big.{Int,Float,Rat) constructors and setters, they are much more natural to use.


>> - I'd prefer a single constructor 'New(int64)' and methods 'SetString', 'SetFloat' etc.
> Not possible. The caller doesn’t know the int64 value. Also, think of how that would look in a chained math statement. Horrible. 

It _is_ possible. You've misunderstood. New(n int64) returns a Fixed that has the _value_ of n, which of course has a different underlying int64 bit pattern in the private Fixed field. The caller want New(42) meaning 42 and does not casre about the internal, scaled value, that's just an implementation detail and no business of the caller. BTW: Chained math statements where the operators are written as function calls, above chains of length 2 are not seen very often. Longer ones, in many cases, well, that's what I'd call horrible.


>> I don't consider comparing performances of 64 bit integer arithmetic and arbitrary sized arithmetic very useful.
> Those are the alternatives to use when performing fixed place arithmetic. In fact decimal.Decimal uses big Int... so it is included for reference. 

The point being made here is fixed size fitting to a machine word on a 64 bit CPU vs arbitrary sizes math libs implemented inevitably by multiple word structs with pointers to backing storage and the necessary allocation overhead. Apples to oranges. Not even in the same league.

--

-j

Jan Mercl

unread,
Nov 29, 2018, 8:28:34 AM11/29/18
to Robert Engels, golang-nuts

On Thu, Nov 29, 2018 at 2:15 PM Robert Engels <ren...@ix.netcom.com> wrote:

> Also, the most important reason against the setters - a Fixed is immutable. 

Great, then just let the setter return a value.

x := fixed.New(0).SetString(foo)

Similarly like big.Float.SetString does and how it's typically used.

The point is that the New should take the type that is expected to be used most often. In your case it would be probably int64, less probably float64 or string.

--

-j

Jamie Clarkson

unread,
Nov 29, 2018, 8:28:40 AM11/29/18
to golang-nuts
I think the logic should be that if either operand is NaN the comparison should be false to match floats (currently it looks like f < NaN and NaN < f  give incorrect results), you might have some reason for that though.

A few other random thoughts: 

- Not all methods/funcs are documented (which might clear up the above). 
- It might be nice to have a clamped set for a floating value out of range (rather than just return a NaN).
- Can't see a way to create from an integer (other than convert to float)
- Personally I'd rather not see a NewS() which can silently fail with no info, I'd prefer to rename NewSErr to Parse and remove NewS.

Other than that, pretty cool.

Cheers,

Jamie

Jamie Clarkson

unread,
Nov 29, 2018, 8:32:10 AM11/29/18
to golang-nuts
Also I fail to see the point of the ToRaw/fromRaw when you could just make FP public.

Jamie Clarkson

unread,
Nov 29, 2018, 8:34:38 AM11/29/18
to golang-nuts
Scratch that, just seen your original reply to Jan about them being temporary.

Robert Engels

unread,
Nov 29, 2018, 8:42:22 AM11/29/18
to Jan Mercl, golang-nuts
Ah, you want a ctor that is the int value. Ok. The Raw doesn’t do this anyway... I could add a NewI() ctor but I’m not sure it is much that NewF(float64(x)) given the magnitude restrictions. 

If you review the gotrader you’ll see that it uses a dot import on this. If it was just Number you lose a lot of information. I find it makes the structures far more readable for a common type. 

Java’s BigInteger has this exact optimization when the value fits in a single word. Go could do this too so it is a fair performance comparison IMO. 

Robert Engels

unread,
Nov 29, 2018, 8:44:31 AM11/29/18
to Jamie Clarkson, golang-nuts
You don’t want fp public for value safety. The Raw methods are going away in favor of direct read and write. 

Robert Engels

unread,
Nov 29, 2018, 8:46:48 AM11/29/18
to Jan Mercl, golang-nuts
Technically, a NaN in comparison with any other including NaN is false. For Cmp this creates a problem.... 
--

Robert Engels

unread,
Nov 29, 2018, 8:48:35 AM11/29/18
to Jan Mercl, golang-nuts
Btw, thus includes == when using NaN and float64. It’s strange but that’s the way it’s defined. 

Jamie Clarkson

unread,
Nov 29, 2018, 8:53:31 AM11/29/18
to golang-nuts
Yeah I posted that before rereading your initial reply to Jan, I can accept the argument of not exposing the underlying type directly (to avoid
using standard int operators on Fixed values), however the raw accessors circumvent that since you could always use them to modify a value through a pointer.  The point is moot though if the raw accessors are going away.

Jan Mercl

unread,
Nov 29, 2018, 8:57:10 AM11/29/18
to Robert Engels, golang-nuts
On Thu, Nov 29, 2018 at 2:41 PM Robert Engels <ren...@ix.netcom.com> wrote:


> Ah, you want a ctor that is the int value. Ok. The Raw doesn’t do this anyway... I could add a NewI() ctor but I’m not sure it is much that NewF(float64(x)) given the magnitude restrictions. 

The OP says: "For those interesting in financial apps, ...". I don't think financial apps prefer using inexact floating point numbers like float64. They tend to rather use _exact_ values like 1234.56. Note that many such values cannot be constructed _exactly_ from a float64 at all. Probably forces the user to workaround using strings. Much more effective would be x := fixed.New(123456).Div(scale), where var scale = fixed.New(100) performed probably in init(). Or even NewScaled(int64, int64). Or even New just should have two int64 parameters. Provided we really talk about financial computing.


> If you review the gotrader you’ll see that it uses a dot import on this. 

Dot imports shall not pass my review.

> If it was just Number you lose a lot of information. 

And that's exactly why dot imports should not ne used.

--

-j

Jamie Clarkson

unread,
Nov 29, 2018, 9:01:46 AM11/29/18
to golang-nuts
Yes it does (for some reason I was thinking that Cmp returning 0 was false but it's not it's equals :) ), I'm not sure Cmp can be implemented in that case, at least without panicing NaN == NaN should be false, the other packages like decimal & big don't seem to have an issue as NaN can't exist?  Could be wrong.

Robert Engels

unread,
Nov 29, 2018, 9:08:10 AM11/29/18
to Jan Mercl, golang-nuts
Wait, you support type inference and not dot imports... I think you should revisit this opinion... also, dot imports are very versatile when changing the implementation without changing a lot of LOC. 

I think the string method is certainly more useful when dealing with external systems or files and constants. I think the scaled ctor is a fine addition though. 

Jan Mercl

unread,
Nov 29, 2018, 9:21:03 AM11/29/18
to Robert Engels, golang-nuts
On Thu, Nov 29, 2018 at 3:07 PM Robert Engels <ren...@ix.netcom.com> wrote:

> Wait, you support type inference and not dot imports... I think you should revisit this opinion...

I believe it's the other way around. Seeing T alone has n bits of information. Seeing foo.T carries more bits of information. So Seeing

x := foo.T{}

for example, tells me about everything what I need to know when I already know what is and why I or anyone else imported package foo.

OTOH, "naked" T from an imported package loses that information and requires to additionally build a mind map from T to foo. That's arguably not simpler.


> also, dot imports are very versatile when changing the implementation without changing a lot of LOC.

Well, then I hope your code reviewer does not share my opinions about dot imports ;-)

BTW: Neither are dot imports allowed inside the Go project itself. IINM, the only exception are tests that were written before the build system (the go command) existed.


--

-j

Robert Engels

unread,
Nov 29, 2018, 9:35:38 AM11/29/18
to Jan Mercl, golang-nuts
I am not referring to variable declarations I am referring to

X := SomeFunc()

Far worse for understanding than having common or ubiquitous structs as dot imports. That way they become “part of the language”

Static imports were added to Java long ago and if Java guys can figure it out I think you can too. It makes constants and ubiquitous types far easier to work with. 

And since you like lingo, more bits of information is not always a good thing. That’s why we have compression, and lossless compression of fixed.Fixed to Fixed is pretty good in my book. 

Robert Engels

unread,
Nov 29, 2018, 9:39:18 AM11/29/18
to Jan Mercl, golang-nuts
That being said, I am not sure why I can use the qualified package name when I have a dot import. This would be helpful for method collision like New()
--

Daniel Kortschak

unread,
Nov 29, 2018, 3:56:04 PM11/29/18
to Robert Engels, Jan Mercl, golang-nuts
In the majority of cases compression does not reduce the number of bits
of information (appreciably).

robert engels

unread,
Nov 29, 2018, 4:03:28 PM11/29/18
to Daniel Kortschak, Jan Mercl, golang-nuts
I’m pretty sure that is not correct, see https://www.maximumcompression.com

Most lossless text compression is > 85 %, and if you use lossy compression (images, audio) it can be 100:1

Jan Mercl

unread,
Nov 29, 2018, 4:08:20 PM11/29/18
to robert engels, Daniel Kortschak, golang-nuts

On Thu, Nov 29, 2018 at 10:02 PM robert engels <ren...@ix.netcom.com> wrote:

> I’m pretty sure that is not correct, see https://www.maximumcompression.com

> Most lossless text compression is > 85 %, and if you use lossy compression (images, audio) it can be 100:1

Dan was talking about quite different bits.
--

-j

Liam

unread,
Nov 29, 2018, 4:18:36 PM11/29/18
to golang-nuts
This made the front page of Hacker News

robert engels

unread,
Nov 29, 2018, 5:11:12 PM11/29/18
to Jan Mercl, Daniel Kortschak, golang-nuts
Would you elaborate? I’ve read the discussion multiple times, and can’t see what other bits you think he is referring to.

Daniel Kortschak

unread,
Nov 29, 2018, 5:12:14 PM11/29/18
to Jan Mercl, robert engels, golang-nuts
That is correct. The number of physical bits used to represent the
information is reduced. The number of bits of information remains the
same except in the case of lossy compression.

If this were not true, I could propose the following compression
protocol: count the number of 1 bits in the uncompressed stream and
represent this as a binary number, recursively apply this to the ones-
count until you have one one bit. This is your compressed data. I have
a truly marvellous decompression scheme to complement this, which this
email is too narrow to contain (damn 80 column limit), but here is the
compressed version, 0x1.

robert engels

unread,
Nov 29, 2018, 5:24:34 PM11/29/18
to Daniel Kortschak, Jan Mercl, golang-nuts
Right and since fixed.Fixed can be reduced to Fixed with no loss of information, it is a great lossless compression - more than 50 %.

Thanks for playing !

Ian Denhardt

unread,
Nov 29, 2018, 5:27:33 PM11/29/18
to Jan Mercl, robert engels, Daniel Kortschak, golang-nuts
I believe Jan is talking about bits in the information-theoretic sense.
I.e, asking "how much do I know?", not "how much space does this take
up?"

Quoting robert engels (2018-11-29 17:10:51)
> Would you elaborate? I've read the discussion multiple times, and can't
> see what other bits you think he is referring to.
>
> On Nov 29, 2018, at 3:07 PM, Jan Mercl <[1]0xj...@gmail.com> wrote:
>
> On Thu, Nov 29, 2018 at 10:02 PM robert engels
> <[2]ren...@ix.netcom.com> wrote:
> > I'm pretty sure that is not correct, see
> [3]https://www.maximumcompression.com
> >
> > Most lossless text compression is > 85 %, and if you use lossy
> compression (images, audio) it can be 100:1
> Dan was talking about quite different bits.
> --
>
> -j
> --
> You received this message because you are subscribed to the Google
> Groups "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to [4]golang-nuts...@googlegroups.com.
> For more options, visit [5]https://groups.google.com/d/optout.
>
> --
> You received this message because you are subscribed to the Google
> Groups "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to [6]golang-nuts...@googlegroups.com.
> For more options, visit [7]https://groups.google.com/d/optout.
>
> Verweise
>
> 1. mailto:0xj...@gmail.com
> 2. mailto:ren...@ix.netcom.com
> 3. https://www.maximumcompression.com/
> 4. mailto:golang-nuts...@googlegroups.com
> 5. https://groups.google.com/d/optout
> 6. mailto:golang-nuts...@googlegroups.com
> 7. https://groups.google.com/d/optout

Daniel Kortschak

unread,
Nov 29, 2018, 5:35:04 PM11/29/18
to robert engels, Jan Mercl, golang-nuts
fixed.Number reduces stutter and is more informative. This was Jan's
point.

In general dot imports are a bad idea when maintaining large code bases
since it requires more work to figure out what is actually providing
the X that has been added to the . imports pool. It also a prevents the
protection you get from importing colliding labels under different name
spaces. foo.X and bar.X; import ( . "foo"; . "bar" ) and you have
problems.

When you change the rules of the game, of course you can win. My point
below says that information is not reduced by lossless compression
(your claim being the opposite). Given that as you show fixed.Fixed is
almost completely compressible without any tricks to 50% its original
size (almost because of the capitalisation, but given the requirement
for exported labels to be capitalised we can invoke context, though...
if you rename your package to Fixed, then you get perfect 50%
compression without context). Now given that dot imports are generally
considered a bad idea in Go and that as you show your label can be
compressed, the conclusion is not that you should compress, but rather
there is room to add additional information; fixed.Number.

Thanks for playing!

robert engels

unread,
Nov 29, 2018, 6:08:12 PM11/29/18
to Daniel Kortschak, Jan Mercl, golang-nuts
The dot imports are useful, and others feel the same https://github.com/golang/lint/issues/179. When you make statements like, 

Now given that dot imports are generally considered a bad idea in Go

It feels a lot like the “cut off the sides of the roast to fit it in the oven”.

As I cited, there were static imports added to Java a long time ago, and it is not a problem. Clearly it can’t be that challenging to Go developers.

In Java you don’t write java.lang.Object… you write Object. Similarly in Go, I don’t write github.com/golang/go/runtime/X I write runtime.X and I don’t write go.int64 I write int64 - why? because int64 is a language specified type.

By using dot imports you can promote user types to standard types.

When writing application code - not systems code - you are often working in a constrained environment that the architect has chosen. The dot imports allow the coding to be close to a DSL. Maybe I create a package ‘common’ that has “wrappers/facades’ for a lot of APIs/packages I want my developers to use, but I don’t want to expose all of the functionality. By using a dot import on the common package, the developer works with a concept “Fixed” - she doesn’t need to know it is maybe a decimal.Decimal behind the scenes, and it has a API limited to the solving the problem domain.

It allows the designer and extra layer of abstraction and simplification. The developers don’t need to import/learn a gazillion packages with hundreds of methods, they import ‘common’ as a facade providing a simplified interface to the system. Having to prefix every type with ‘common' is senseless. In reality there would probably be multiple packages, like ‘common’, ‘printing’, ‘security’, ‘database' etc. but even then the name collision would be limited by the designer. This is enterprise/framework design 101.





--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Jan Mercl

unread,
Nov 29, 2018, 6:24:23 PM11/29/18
to robert engels, golang-nuts

On Fri, Nov 30, 2018 at 12:07 AM robert engels <ren...@ix.netcom.com> wrote:

> The dot imports are useful, and others feel the same https://github.com/golang/lint/issues/179

Not all others do feel the same. Actually, most others do not. From Go Code Review Comments:

        This page collects common comments made during reviews of Go code, so that a single detailed
        explanation can be referred to by shorthands. This is a laundry list of common mistakes, not a
        comprehensive style guide.

And in particular: Import Dot

""""
The import . form can be useful in tests that, due to circular dependencies, cannot be made part of
the package being tested:

        package foo_test
        import (
                "bar/testutil" // also imports "foo"
                 . "foo"
        )

In this case, the test file cannot be in package foo because it uses bar/testutil, which imports foo.
So we use the 'import .' form to let the file pretend to be part of package foo even though it is not. Except
for this one case, do not use import . in your programs. It makes the programs much harder to read
because it is unclear whether a name like Quux is a top-level identifier in the current package or in
an imported package.
""""

--

-j

Nigel Tao

unread,
Nov 29, 2018, 6:26:47 PM11/29/18
to Jan Mercl, ren...@ix.netcom.com, golan...@googlegroups.com
On Thu, Nov 29, 2018 at 9:20 PM Jan Mercl <0xj...@gmail.com> wrote:
> - To me type name 'fixed.Fixed' sounds like Javaism.

Well, there's already context.Context, hash.Hash, image.Image and
time.Time in the standard library. Perhaps, in a parallel universe, we
could have settled on the testing.T naming style, but that's all
academic, at this point.

FWIW, in the golang.org/x/image/math/fixed package, the qualified type
name is fixed.Int26_6 and a constructor function is fixed.I.

https://godoc.org/golang.org/x/image/math/fixed

Dan Kortschak

unread,
Nov 29, 2018, 6:34:06 PM11/29/18
to robert engels, Jan Mercl, golang-nuts
On Thu, 2018-11-29 at 17:07 -0600, robert engels wrote:
> The dot imports are useful, and others feel the same https://github.c
> om/golang/lint/issues/179
> <https://github.com/golang/lint/issues/179>. When you make statements
> like, 
>
> >
> > Now given that dot imports are generally considered a bad idea in
> > Go
> It feels a lot like the “cut off the sides of the roast to fit it in
> the oven”.
>
> As I cited, there were static imports added to Java a long time ago,
> and it is not a problem. Clearly it can’t be that challenging to Go
> developers.

Just because something existed somewhere else with different context
(where is may or may not have been a good idea) means that it will be a
good idea here.

> In Java you don’t write java.lang.Object… you write Object. Similarly
> in Go, I don’t write github.com/golang/go/runtime/X
> <http://github.com/golang/go/runtime/X> I write runtime.X and I don’t
> write go.int64 I write int64 - why? because int64 is a language
> specified type.

There's a significant difference here. In go we don't continually
reiterate the import path is is unfortunately the case in java. That
additional verbosity is possibly a reason why it might have been a good
idea in java, though eliding the redundant path information at the call
site probably would have been better.

> By using dot imports you can promote user types to standard types.

And in my experience this leads to additional cognitive load on the
maintainer. A similar argument exists for why operator overloading
doesn't exist in Go.

> When writing application code - not systems code - you are often
> working in a constrained environment that the architect has chosen.
> The dot imports allow the coding to be close to a DSL. Maybe I create
> a package ‘common’ that has “wrappers/facades’ for a lot of
> APIs/packages I want my developers to use, but I don’t want to expose
> all of the functionality. By using a dot import on the common
> package, the developer works with a concept “Fixed” - she doesn’t
> need to know it is maybe a decimal.Decimal behind the scenes, and it
> has a API limited to the solving the problem domain.

The labels make close to zero difference here. If the user wants to
make a dsl, they can do that, but don't expect me to consume their
code.
> > > > On Nov 29, 2018, at 4:11 PM, Daniel Kortschak <daniel@kortschak

Jan Mercl

unread,
Nov 29, 2018, 6:36:00 PM11/29/18
to Nigel Tao, golan...@googlegroups.com
On Fri, Nov 30, 2018 at 12:26 AM Nigel Tao <nige...@golang.org> wrote:

On Thu, Nov 29, 2018 at 9:20 PM Jan Mercl <0xj...@gmail.com> wrote:

> Well, there's already context.Context, hash.Hash, image.Image and
> time.Time in the standard library. Perhaps, in a parallel universe, we
> could have settled on the testing.T naming style, but that's all
> academic, at this point.

Those names are set in stone by the Go 1 compatibility guarantee. The interesting, but hypothetical, question is, if possible and revisited now, whether a better name would be chosen. It's not always possible, I guess. Still, when it _is_, the more informative type name should be IMO preferred. big.{Int,Float,Rat} was already mentioned, but it should be added, that the math/big package started with only the big.Int type, not big.Big or big.BigtInt type.


--

-j

Bakul Shah

unread,
Nov 29, 2018, 10:14:23 PM11/29/18
to robert engels, golang-nuts
FWIW, in some code I am writing, I considered using 
fixed point decimal numbers but ended up defining a
*currency* type which is an int64 for value + a separate
unit + currency kind. Even if I use a unit of millicent, this
will allow handling amounts close to $100 Trillion. I
don't expect this limit to be a problem for my personal
finances! Performance is not an issue for my use. I
even store all the financial data in text files!

Dealing with stuff such  as currency conversion, interest
rates, stocks etc. gets a bit complicated due to their own
precision needs but for that one can look at existing
practices to do the right thing (which is, be able to accurately
implement the rules your bank etc use).

[Aside:
Ideally this would be done using a *generic* currency
type. Something like

import "currency"
type $ = currency.Type("$")
type £ = currency.Type("£")

var m1 = $(5)
var m2 = $(10)
var m3 = £(2)

m1 + m2 // ok
m2 + m3 // compile time error
m1*m2 // compile time error
m1*5 // ok
m1+5 // compile time error

I doubt go2 will get generics flexible enough for this!
]

Robert Engels

unread,
Nov 29, 2018, 10:22:12 PM11/29/18
to Bakul Shah, golang-nuts
Yes, a real currency type would contain a Fixed. Most likely adding a symbol, code, etc. 

Ian Denhardt

unread,
Nov 29, 2018, 11:01:58 PM11/29/18
to Bakul Shah, robert engels, golang-nuts
Quoting Bakul Shah (2018-11-29 22:13:45)

> I doubt go2 will get generics flexible enough for this!

It can actually already pull much of this off, and you don't even need the
contracts part of the draft. E.g:

// Type 'Money' represents a monetary value. The Currency type parameter
// is unused at runtime, but serves to prevent mixing up different
// currencies:
type Money(type Currency) int64

// These types used as the parameter for 'Money'. They are never used as
// values, just as part of this trick with the type system -- because of
// this you'll sometimes hear them referred to as "phantom types".
type USD struct{}
type EUR struct{}
type GBP struct{}
// ...

var m1 Money(USD) = 5
var m2 Money(USD) = 10
var m3 Money(EUR) = 2

m1 + m2 // ok
m2 + m3 // type error: Money(USD) vs. Money(EUR)

-Ian

Bakul Shah

unread,
Nov 30, 2018, 9:44:19 AM11/30/18
to Ian Denhardt, robert engels, golang-nuts
This is quite clever but you really don’t want to have to define types
for all of the currencies in use where most people deal with one or
few at most. Worse, new currencies may come into existence at any
time. And consider securities such as stocks, bonds etc. which have
some similar operations but if you buy shares of different companies,
they must be kept separate. Accidentally adding Apple shares to
IBM shares should be flagged. New companies get listed all the time
so you really want an ability to create and use dynamic instances of a
generic type. In the absence of such you can always write verification
code to avoid such mistakes. I was just musing that it sure would be
nice to be able to have the compiler do such checking.

Anthony Martin

unread,
Dec 1, 2018, 3:17:16 AM12/1/18
to Nigel Tao, Jan Mercl, ren...@ix.netcom.com, golan...@googlegroups.com
Nigel Tao <nige...@golang.org> once said:
> Well, there's already context.Context, hash.Hash, image.Image and
> time.Time in the standard library.

All of which are not great. Better names are context.Frame,
hash.Function, raster.Image, and time.Instant.

Anthony

Michael Jones

unread,
Dec 1, 2018, 3:29:09 AM12/1/18
to al...@pbrane.org, Nigel Tao, Jan Mercl, Robert Engels, golang-nuts
Which reminds me...these kinds of name changes deserve a thread/list in the Go2 proposals

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


--
Michael T. Jones
michae...@gmail.com

Dan Kortschak

unread,
Dec 1, 2018, 3:47:23 AM12/1/18
to Anthony Martin, Nigel Tao, Jan Mercl, ren...@ix.netcom.com, golan...@googlegroups.com
Very nice.

Robert Engels

unread,
Dec 1, 2018, 7:53:43 AM12/1/18
to Dan Kortschak, Anthony Martin, Nigel Tao, Jan Mercl, golan...@googlegroups.com
hash.Function? Then you need print.Function. So soon enough there will be a 1000 interfaces named Function that have nothing to do with hashing or printing because everything can be decomposed that way.

context.Frame? Now you’re just being silly. You might as well use context.Instance or even Function...

This an issue because of the reluctance to properly support the dot import (or something like it). Then you would just have Context, which for core types is what you want. If you have a specialized Context then it should be fully qualified.

It’s comforting to know that Go will end with 1.x because 2.0 is going to be designed by committee and is going to stink. Too many people talking just to hear themselves.

Forget the “rules” and focus on writing software that is easy to read, write and maintain.

You’ll know it when you see it. APIs are hard. Leave it to the practical professionals.

Robert Engels

unread,
Dec 1, 2018, 8:04:41 AM12/1/18
to Dan Kortschak, Anthony Martin, Nigel Tao, Jan Mercl, golan...@googlegroups.com
And while we’re at it, take a closer look at the hash package https://golang.org/pkg/hash/#Hash

Especially the example and the doc surrounding it. 

Why doesn’t Hash “extend” the binary marshall interfaces? Why the need to cast if the documentation says it does?

There are a lot of low hanging fruit that should be corrected in Go 2 that should be easily agreed upon. 

Robert Engels

unread,
Dec 1, 2018, 8:14:16 AM12/1/18
to Dan Kortschak, Anthony Martin, Nigel Tao, Jan Mercl, golan...@googlegroups.com
And let’s keep going, the sha256 package handles sha256 and sha224 ? Huh?

And the NewX return hash.Hash rather than the concrete type - which would be fine except for the implicit Marshall interfaces. If they returned the concrete type I could have my own interface HashWithMarshall it could be assigned to and compiled time type checked. 

Just face it. There are a lot of issues to be address in Go. There are currently 4000 open issues. 

Maybe Go 2 should be tabled and some of the lower hanging fruit attacked, especially issues that can be address without breaking backwards compatibility. 

Jan Mercl

unread,
Dec 1, 2018, 8:48:55 AM12/1/18
to Dan Kortschak, golan...@googlegroups.com
On Sat, Dec 1, 2018 at 9:47 AM Dan Kortschak <d...@kortschak.io> wrote:

> Very nice.

--

-j

Robert Engels

unread,
Dec 1, 2018, 9:00:48 AM12/1/18
to Jan Mercl, Dan Kortschak, golan...@googlegroups.com
This is a problem with it seems more than a few in this community. Someone makes a criticism of an idea, backs it up, and is treated in a childish manner because it doesn’t go along with dominate opinions of the controllers of the group think. Not good IMO. 
--

Robert Engels

unread,
Dec 1, 2018, 9:11:55 AM12/1/18
to Jan Mercl, Dan Kortschak, golan...@googlegroups.com
Sorry, more than few is too many. Few is more appropriate.

And before this goes farther. It was a thread I started to announce a project some might find useful or interesting. 

It was hijacked by the powers and turned into something else. 

It wasn’t the point, nor what I wanted. 

Michael Jones

unread,
Dec 1, 2018, 9:18:01 AM12/1/18
to Robert Engels, Jan Mercl, d...@kortschak.io, golang-nuts
I can see why you're feeling that. My sense is that what causes flare-ups is when people (universally rather than just here) get the feeling that the discussion is becoming personal rather than technical. Go is lots of people's work / tool / job / fun / etc., so passions may be strong, but I've never seen any tendency to be angry and protective about people raising issues with poor choices. What does come up is the desire for examples, use-cases, and other kinds of supporting information so that the conversation moves from "i feel that" ==> "in these situations" ==> "a workable resolution" ==> ... ==> "X seems the best way forward after much analysis." That made go great so far, and the conversations generally collegial.

Anthony Martin

unread,
Dec 1, 2018, 9:22:41 AM12/1/18
to Robert Engels, Dan Kortschak, Nigel Tao, Jan Mercl, golan...@googlegroups.com
A hash function is a mathematical construction.
A raster image is a type of graphical representation.
A time instant is a single point in a duration.

All of these are clear as can be.

I'll give you "context frame". It is a bit wonky.
A better phrase is out there somewhere. Scope,
environment, etc. could work but it also must
convey be carried or passed around.

We have lots of English words. Why not use them?

Anthony

Michael Jones

unread,
Dec 1, 2018, 9:23:05 AM12/1/18
to Robert Engels, Jan Mercl, d...@kortschak.io, golang-nuts
I'll also add that personally I saw a lot of "change this" commentary with rather little "thank you." That was not in the best spirit of what (at at least I think) it should be. It's always good to act on ideas, build solutions, and share results. It would be great if everyone always started with that. You deserve it as does your code.

Thank you!
Michael

Robert Engels

unread,
Dec 1, 2018, 9:52:33 AM12/1/18
to Michael Jones, Jan Mercl, d...@kortschak.io, golang-nuts
I disagree with a few of those, but the simplest example is image. There still needs to be an image.Image interface for methods common to all image types. Then sub packages for raster and vector (others?)  There are too many different image types so you end up a multitude of packages and since package names need to be short you lose a lot of information. An easy example is fractal. There are fractal images do you would have fractal.Image but most people seeing a package called fractal would assume it handle fractals.... 

Personally I would like to look at the documentation for the image package to quickly see all of the supported types. 

Thus is why having a “rule” is tough to do in practice. 

Michael Jones

unread,
Dec 1, 2018, 10:34:29 AM12/1/18
to Robert Engels, Jan Mercl, d...@kortschak.io, golang-nuts
I need to do a thorough summary, but as an example of things that could usefully change (perhaps) for improved coherence and excellence in Go2 through naming and simple rewrites, consider the image package.

1. The name of the package (fine always it seem, but in some cases maybe a change would help)
2. The name of functions and methods (fine generally, but a few oddities exist here and there)
3. The approach to special/exceptional actions. Image has a glaring issue here, described below.

The way options/defaults/configurations are set varies between JPEG, PNG, and TIFF such that in one case you set an option via a method call, one via a "constructor" like list, one via a configuration struct that you pass in, and one voa a config struct that you call methods against.

Any of these is fine to me, but having a variety of them is testament to the addition of image format support at different times by different hands and it would seem sad if Go17 was still this way. Instead, now that examples exist for each style, why not pick one of them (any one!) and convert the others to the same scheme. I know itr would be arbitrary breakage now, but that's paying forward for technical debt. When 1M Go developers becomes 10M, we'll have at 9M happy campers who never realized it was "wild" (disharmonious, unmutual) in the old days.

4. The approach to error labeling and categorization.
5. The "meta-ness" of the API. Hard to find the right way to say this, but for example, the filesystem APIs presume you're using the system native filesystem--naturally. But if you need to do all the same work but in a ZIP file, or in a custom binary blob with a file system inside, that ends up being a parallel construct rather than being able to say os.Open is the null case of MyFileSystem.Open with everything else in the filesystem API there naturally, or settabble in some way.

Not arguing for any of these points here, just sharing that name polishing, unjustified variety in same-notion aspects of the API, and "only now do we realize that" generalizations all seem perfect subjects for careful review as part of the Go2 process. I'm in favor of the thought you're raising. Also, on the name front, it is not only the "package.Function()" that developers see, but hopefully more often it is variable.Method() and in case any package-name-based stuttering is gone. This is a subtle motivation to look for designs/refactorings that are heavy on methods on package types. (well...I think it is. there may not be consensus on that) 

Michael

Ian Lance Taylor

unread,
Dec 1, 2018, 12:49:22 PM12/1/18
to Jan Mercl, golang-nuts
Please stay polite, and please follow the code of conduct
(https://golang.org/conduct). Thanks.

Ian

Louki Sumirniy

unread,
Dec 5, 2018, 9:46:27 PM12/5/18
to golang-nuts
I agree about the stutter naming. The name should at least specify the bitwidth of the integral and fractional parts, to follow the pattern of the fixed width types already existing. Fixed is easy enough to infer that it is a fixed point type, by the package. You could name the types based on the format convention - such as the precision used in various financial fields, and related to the currency, for example the difference between Yen and Dollars, the former not including a fractional value at all (The japanese are of course better at mathematics than most of the rest of the world, their approach is better and also used in several other currencies like the Serbian Dinar, though in Serbia they still have 2 decimal places in the accounting despite the non-existence of fractional dinars).

Also it makes sense to me that you would embed the type inside a struct, as these parameters would need to be available in order to provide multiple bit width parameter types (and would be necessary, for example, in forex trading there is  a name 'pip' meaning some number of  decimal places), and I think that obviously, if one were to instead use an alias for uint64 or whatever, that the users of the library could make the mistake of using inbuilt math operators on it and get entirely wrong results, whereas the struct type implicitly will compile-time refuse to play that game.

On Thursday, 29 November 2018 14:21:21 UTC+1, Jan Mercl wrote:
On Thu, Nov 29, 2018 at 2:00 PM Robert Engels <ren...@ix.netcom.com> wrote:


>> - To me type name 'fixed.Fixed' sounds like Javaism. Go code usually tries to avoid such stutter: 'sort.Interface', 'big.Int' etc.
> To me that’s a limitation of Go with small packages like this that only have a single public struct. It is based on decimal.Decimal so I’m not the only one who thinks this....

I don't think we are talking about the same thing here. Go idiom is to name types such that they are not the same as the package qualifier (modulo case) at the caller site. So the exported type should be 'Int', or 'Float' or 'Real' or 'Number', etc., not 'FIxed' to avoid 'fixed.Fixed' at caller site. `var n fixed.Number` looks better to me, for example, than `var n fixed.Fixed`. The later actually does not even communicate any hint what the type could possibly be.

>> - A struct with a single field could be replaced by the field itself. OTOH, it would enable coding errors by applying arithmetic operators to it directly, so it's maybe justified in this case if that was the intention.
> It was the intention. The Raw methods are there temporarily and will be removed for direct serialization via a Writer. 

Then it looks strange that to construct a Fixed from int64 one has to write 'fixed.NewF(0).FromRaw(42)'. Check the big.{Int,Float,Rat) constructors and setters, they are much more natural to use.

>> - I'd prefer a single constructor 'New(int64)' and methods 'SetString', 'SetFloat' etc.
> Not possible. The caller doesn’t know the int64 value. Also, think of how that would look in a chained math statement. Horrible. 

It _is_ possible. You've misunderstood. New(n int64) returns a Fixed that has the _value_ of n, which of course has a different underlying int64 bit pattern in the private Fixed field. The caller want New(42) meaning 42 and does not casre about the internal, scaled value, that's just an implementation detail and no business of the caller. BTW: Chained math statements where the operators are written as function calls, above chains of length 2 are not seen very often. Longer ones, in many cases, well, that's what I'd call horrible.

>> I don't consider comparing performances of 64 bit integer arithmetic and arbitrary sized arithmetic very useful.
> Those are the alternatives to use when performing fixed place arithmetic. In fact decimal.Decimal uses big Int... so it is included for reference. 

The point being made here is fixed size fitting to a machine word on a 64 bit CPU vs arbitrary sizes math libs implemented inevitably by multiple word structs with pointers to backing storage and the necessary allocation overhead. Apples to oranges. Not even in the same league.

--

-j

Louki Sumirniy

unread,
Dec 5, 2018, 9:52:33 PM12/5/18
to golang-nuts
mmmmm!

Yes, using pure integer values for currency is generally the best way to deal with it. The Bitcoin standard is that 1 BTC = 10,000,000 'satoshis'. Many rightly point out that this is a stupidly small precision, but for the time being is ok. I  personally would expand this, and just use a 32/32 split and thus a 2^32 width for the fractional precision, which also makes for very easy, fast implementation. I don't really think that this would cause any issues whatsoever, really the differences between currencies would then merely be a matter of presentation, as 32 bits of fractional precision will not be exceeded in any near future situation.
Reply all
Reply to author
Forward
0 new messages