Blog Post

#372 Build 1.0.33

brian Mon 13 Oct 2008

The latest build is posted and the online docs are updated.

This build contains two massive changes:

  • Nullable types
  • Java runtime has been refactored to use java.lang types

Note that none of the documentation has been updated to include nullable types (although the new APIs docs are nullable aware).

Nullable Types

Fan's type system now includes support for nullable. Types may be nullable or non-nullable. A non-nullable type is guaranteed to never store the null value. Nullable types are indicated with a trailing "?". This means non-nullable is the default unless otherwise specified:

Str   // never stores null
Str?  // might store null

Impact

As might be expected nullable types have a massive impact on the codebase and all public APIs. You can browse the new fandocs to see how nullables fit into the APIs. The statistics for APIs which use nullable types:

types:    553
slots:   6375 1022  16.0%
fields:  2050  296  14.4%
methods: 4325  726  16.7%
returns: 4325  348   8.0% (methods which return nullable)

So only 16% of all the slots use nullable types. Only 8% of all methods return a nullable type. So empirical evidence does indeed suggest that non-nullable is the common case.

Reflection

The sys::Type class supports three new methods:

Str?#.isNullable     =>  true
Str#.toNullable      =>  sys::Str?
Str?#.toNonNullable  =>  sys::Str

Type Inference

The following operators result in a nullable typed expression:

  • safe navigation: ?. ?-> ?:
  • dynamic invoke: ->
  • the as operator
  • calling any method which is declared to return a nullable type
  • accessing any field which is declared as a nullable type

List and map type inference now take nullable into account:

[,]       =>  Obj?[,]
[3]       =>  Int[3]
[3,null]  =>  Int?[3, null]
[:]       =>  Obj:Obj?[,]   // keys can never be null
[3:null]  =>  Int:Obj?[...]

Note that the empty list and map default to Obj? unless otherwise specified. Also note that if you declare a list literal that might store nulls, you will likely need an explicit type signature. So far this seems to be the most annoying thing about the nullable type system - especially with regard with writing tests.

But you will get a compiler error if you try to add null to a non-nullable list:

fansh> list := [2,3,4]
[2, 3, 4]
fansh> list.add(null)
ERROR(6): Invalid args add(sys::Int), not (null)

Generic types can be parameterized as nullable or non-nullable:

fansh> list := Str[,]
fansh> list.type.method("add").signature
sys::Str[] add(sys::Str item)
fansh> list.type.method("peek").signature
sys::Str? peek()

fansh> list := Str?[,]
fansh> list.type.method("add").signature
sys::Str?[] add(sys::Str? item)

Nullable Compiler Checks

The following rules are currently enforced by the compiler:

  1. You cannot use the null literal where a non-nullable type is expected
  2. You cannot compare a non-nullable type for null: x == null
  3. You cannot use the null-safe operators ?. ?-> ?: against a non-nullable type

These rules seems do a really good job of enforcing slots declarations to correctly use nullable. They was some debate if the latter two rules made sense - I definitely think they do. The only cases where the current code base was using those operators was:

  • to check for null arguments (which will go away)
  • lazily loading of fields (better replaced using once methods)
  • because the type signature was incorrect (fixed by making type nullable)

I have not implemented any runtime checks yet. This will be the next step I'll work on as part of the value types feature.

With the basic infrastructure in place, please play around with the current rules and hack up new rules. This is just the starting point so that we have a baseline to experiment with.

Java Runtime

Previous the Java runtime mapped Fan objects into subclasses of fan.sys.FanObj and used Fan specific wrappers for every class. Now Fan types map to Java classes as follows:

sys::Obj     =>  java.lang.Object
sys::Bool    =>  java.lang.Boolean
sys::Str     =>  java.lang.String
sys::Num     =>  java.lang.Number
sys::Int     =>  java.lang.Long
sys::Float   =>  java.lang.Double
sys::Decimal =>  java.math.BigDecimal

This change is the first step to better Java library interop. With this change you can now call Fan APIs and leverage javac's support for boxing/unboxing.

The .NET runtime will mimic these changes but requires value-type support in Fan before moving forward.

Change Log

Build 1.0.33 (13 Oct 08)

  • Java runtime: fan.sys.Obj -> java.lang.Object
  • Java runtime: fan.sys.Bool -> java.lang.Boolean
  • Java runtime: fan.sys.Int -> java.lang.Long
  • Java runtime: fan.sys.Float -> java.lang.Double
  • Java runtime: fan.sys.Num -> java.lang.Number
  • Java runtime: fan.sys.Decimal -> java.math.BigDecimal
  • Nullable type support
  • Support for parameterized fields (sys::Map.def)
  • Add build::CreateZip task, buildall.fan handles dist zip
  • Fix RichText editor home/end to take whitespace into account
  • Change fwt::Image constructors
  • Add icons to flux history items

katox Tue 14 Oct 2008

A frenzy pace of implementation I must say, cool! The tip is compilable again.

I have 16 failing tests in ./buildall.fan full test suite for v1.0.33 - I'm not sure if this is expected or not. The sidewalk and flux failing tests probably are but inet::UdpSocketTest.testOptions?

I'd suggest a standard facet for fant named @knownFailingTest or something similar. I break things pretty often and it is so easy to miss a new error in many others...

brian Tue 14 Oct 2008

I have 16 failing tests in ./buildall.fan full test suite for v1.0.33

I'm not seeing any test failures - I expect the full test to pass (on the JVM, not necessarily on CLR).

How do you even have sidewalk tests?

katox Tue 14 Oct 2008

I don't ;). But the build scripts try to run them apparently:

Time: 20225ms

Failed:
inet::UdpSocketTest.testOptions
flux::FindInFilesTest.test
flux::FluxUtilTest.testMark
testCompiler::ScriptTest.testCompile
sidewalk::NsTest.testNs
sidewalk::NsTest.testObjUri
sidewalk::NsTest.testGet
sidewalk::PermTest.testUserList
sidewalk::PermTest.testUser
sidewalk::PermTest.testTopic
sidewalk::PermTest.testReportList
sidewalk::PermTest.testSearchList
sidewalk::PermTest.testBlogList
sidewalk::UserColorMapTest.test
sidewalk::UtilTest.testDateFormats
sidewalk::UtilTest.testToDisplay

***
*** 16 FAILURES [98 tests, 762 methods, 2652601 verifies] 

I didn't pay too much attention if they are there and incomplete or missing completely (which is the case):

TEST FAILED
sys::Err: java.lang.NoClassDefFoundError: fan/sys/Obj
sidewalk::NsTest.makeTestNs (NsTest.fan:123)
sidewalk::NsTest.testNs (NsTest.fan:32)
sun.reflect.NativeMethodAccessorImpl.invoke0 (NativeMethodAccessorImpl.java)
sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:39)
sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:25)
java.lang.reflect.Method.invoke (Method.java:597)
fan.sys.Method.invoke (Method.java:554)
fan.sys.Method$MethodFunc.call (Method.java:222)
fan.sys.Method.call (Method.java:178)
fanx.tools.Fant.runTest (Fant.java:171)
fanx.tools.Fant.test (Fant.java:92)
fanx.tools.Fant.test (Fant.java:58)
fanx.tools.Fant.test (Fant.java:29)
fanx.tools.Fant.run (Fant.java:252)
fanx.tools.Fant$1.run (Fant.java:293)
fan.sys.Thread$Val.run (Thread.java:447)
-- Run:  sidewalk::NsTest.testObjUri...

What is more interesting that the compilerTest fails occasionally (not sure about the conditions).

TEST FAILED
sys::TestErr: Test failed: test_0::Foo === test_0::Foo
fan.sys.Test.err (Test.java:160)
fan.sys.Test.fail (Test.java:152)
fan.sys.Test.verifyNotSame (Test.java:122)
fan.sys.Test.verifyNotSame (Test.java:116)
testCompiler::ScriptTest.testCompile (ScriptTest.fan:31)
sun.reflect.NativeMethodAccessorImpl.invoke0 (NativeMethodAccessorImpl.java)
sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:39)
sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:25)
java.lang.reflect.Method.invoke (Method.java:597)
fan.sys.Method.invoke (Method.java:554)
fan.sys.Method$MethodFunc.call (Method.java:222)
fan.sys.Method.call (Method.java:178)
fanx.tools.Fant.runTest (Fant.java:171)
fanx.tools.Fant.test (Fant.java:92)
fanx.tools.Fant.test (Fant.java:58)
fanx.tools.Fant.test (Fant.java:29)
fanx.tools.Fant.run (Fant.java:252)
fanx.tools.Fant$1.run (Fant.java:293)
fan.sys.Thread$Val.run (Thread.java:447)

adding ["force":true"] makes the test to pass however (and wrong obviously ;).

The inet test trace is here (I haven't examined it):

TEST FAILED
sys::TestErr: Test failed: 131071 != 221184
fan.sys.Test.err (Test.java:160)
fan.sys.Test.fail (Test.java:152)
fan.sys.Test.verifyEq (Test.java:80)
fan.sys.Test.verifyEq (Test.java:74)
inet::UdpSocketTest.testOptions (UdpSocketTest.fan:219)
sun.reflect.NativeMethodAccessorImpl.invoke0 (NativeMethodAccessorImpl.java)
sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:39)
sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:25)
java.lang.reflect.Method.invoke (Method.java:597)
fan.sys.Method.invoke (Method.java:554)
fan.sys.Method$MethodFunc.call (Method.java:222)
fan.sys.Method.call (Method.java:178)
fanx.tools.Fant.runTest (Fant.java:171)
fanx.tools.Fant.test (Fant.java:92)
fanx.tools.Fant.test (Fant.java:58)
fanx.tools.Fant.test (Fant.java:29)
fanx.tools.Fant.run (Fant.java:252)
fanx.tools.Fant$1.run (Fant.java:293)
fan.sys.Thread$Val.run (Thread.java:447)

Used environment: Sun JDK 1.6.0_07 (32bit, Linux 2.6.24, i686 SMP).

BTW I forgot closing quote on line 72 of this comment:

adding '["force":true"] makes the test to pass however (and wrong obviously ;).

and the preview worked but the submit reported an error - just pointing this out...

brian Tue 14 Oct 2008

I don't know how you got sidewalk stuck in there - maybe from an old build. You should nuke the sidewalk modules from your lib/java and lib/fan directories. But it definitely isn't in the newer build, so you must have some of the old stuff mixed up with the new stuff. Can you check that?

Some of those other problems might actually be JVM version/OS issues that we need to dig into. I'll try with 1.6 tonight and also on a Mac and let's see if we can get a clean tip that has the entire test suite passing. That needs to be the #1 priority before any new features.

katox Tue 14 Oct 2008

Right, I only examined the sources (hg status reported a clean tree). There must have been some old libraries from an older build. I thought that target lib is completely purged on buildall.

I replaced rel by a pristine v1.0.33 and also the complete lib in dev (I only edited sys.props). The sidewalk tests disappeared (I still wonder how the depend on lib) the rest of problems is the same (+ same symptoms):

Time: 30505ms
Failed:
  inet::UdpSocketTest.testOptions
  flux::FindInFilesTest.test
  flux::FluxUtilTest.testMark
  testCompiler::ScriptTest.testCompile

katox Wed 15 Oct 2008

The flux should be fixed now - or if I messed up the patch the problem is known at least ;). ScriptTest is already fine in current tip. UdpSocketTest - the failure goes away when the tip is compiled by buildboot and buildpods separately (full test).

I spotted another thing. When there was a problem in flux/flux tests then the "./build.fan full test" stops there and it doesn't run the tests in flux/fluxText. That's rather confusing because there wasn't a compile error (just a test failure). If there was a problem in the second part of the test suite it would remain hidden.

brian Fri 17 Oct 2008

Katox sent me a patch which I've pushed into the master repo for one of the tests. Sounds like my patches for the other tests resolved the remaining failed tests. So I think all tests are passing now.

I spotted another thing. When there was a problem in flux/flux tests then the "./build.fan full test" stops there and it doesn't run the tests in flux/fluxText.

Depending on what build targets you are running a test failure is considered a fatal build error and will consider the entire build failed. For example a test failure while building the dist is considered fatal.

Note that running "buildall test" runs "fan -all" which will run all the tests at once and reports all the results even if some pods fail. However running something like "buildpods test" is convenience for running the tests for each pod individually (and will stop as soon as one fails).

katox Fri 17 Oct 2008

Katox sent me a patch which I've pushed into the master repo for one of the tests.

Pushed ;) ... you rewrote it to a better style, completed the Mark contract and added some tests ;). I'm looking forward to Fan's bright and bugless future.

So I think all tests are passing now.

Yep. All tests in tip pass neatly. Thanks for explanations I'm still not very used to Fan's build system.

Login or Signup to reply.