#2808 Compare object types

Dan B Wed 22 Jul 2020

I am struggling to do a comparison of objects without the compiler complaining. I know how to do this in C#, but not Fantom (new to Fantom); please help.

class Boomtown {
  // Objects entered into foo are classes which inherit Foo class
  Foo?[]   foo := Foo?[,].fill(8,null)

  internal Future? doSomething( Foo something ) {
    // Does foo have items?
    haveFoo := foo.findAll | v | { v != null } 

    if( haveFoo.size > 0 ) {
      // This where I start to get lost
      foundDiff := haveFoo.each | v | {
        if( v isnot something ) {   // Compiler doesn't like this: Unknown type 'something'
          return true
        }
    }
    ...
  }
}

Thanks in advance for any help.

SlimerDude Wed 22 Jul 2020

Hi Dan B,

You didn't say what the compiler errors were - but I found a few - a couple being:

  • it's Foo?[,].fill(null, 8) - not Foo?[,].fill(8,null)
  • foundDiff := haveFoo.each - each() doesn't return anything

And something would need to be a defined class like Foo.

For what you want, I think you'll need to use List.all() or List.any(), like this:

class Boomtown {
    // Objects entered into foo are classes which inherit Foo class
    Foo?[]   foo := Foo?[,].fill(null, 8)
    
    internal Obj? doSomething( Foo something ) {
        
        // Does foo have items?
        haveFoo := foo.all |v| { v == null || v is Foo } 

        echo(haveFoo)

        return null
    }
}
    
class Foo { }

Dan B Wed 22 Jul 2020

I just tried the following; which results in the same compiler error.

| MicroTask[] Tasks, MicroTask Task->Bool | okayToRun

okayToRun = | Foo[] Tasks, Foo Task->Bool | {
  Tasks.each | t | {
    if( t isnot Task ) {
      return false
    }
  }

  return true
}

...
canRun := okayToRun( haveFoo, something )

Dan B Wed 22 Jul 2020

SlimerDude, thanks for the reply. I posted my second without first refreshing the page. I will try your solution.

The foo init was my mistake in creating the question. The real code has the correct int. I was trying to keep my question generic

private MicroTask?[] activeMicroTasks := MicroTask?[,].fill(null, 8)

Dan B Wed 22 Jul 2020

I don't think I have asked the question correctly or provided the correct background.

We have the following classes

  • MicroTask: very basic just holds an integer 0 to 7
  • AmAcWrTask: Inherits MicroTask (i.e. class AmAcWrTasl : MicroTask)
  • AutoConfigTask: Inherits AmAcWrTask
  • AutoMatchTask: Inherits AmAcWrTask
  • WireResistanceTask: Inherits AmAcWrTask
  • SyncConfigTask: Inherits MicroTask

Then in another class

private MicroTask?[]    activeMicroTasks        := MicroTask?[,].fill(null, 8)
private MicroTask?[]    delayedMicroTasks       := MicroTask?[,].fill(null, 8)

// Run a task for a particular micro
internal Future? runMicroTask( MicroTask task, Bool checked := true, Bool autoStatus := true ) {

  // Input arg "task" can be one of: AutoConfigTask, AutoMatchTask, SyncConfigTask, or WireResistanceTask
  // Class variable "activeMicroTasks" will contain the "task" at the correct index later in this function.

  //  I am trying to detect when runMicroTask is called with a different task type than what task type(s)
  //  is contained in activeMicroTasks.  
  //  If it is different the incoming task is placed in delayedMicroTasks, to be run later 

  ...
}

The type checking in the background function works as expected.

// Background function, runs periodically
Void backgrouFunc() {
   ...

   else {
     //  SAGE-1907: Maestro fails to send POST to start WR 
     haveActive    := activeMicroTasks.findAll | v | { v != null }
     haveDelayed   := delayedMicroTasks.findAll | v | { v != null } 

     //  Do we have any task that were delayed?
     if( ( haveActive.size == 0 ) && ( haveDelayed.size > 0 ) ) {          
       Device.log.info( "[BG Thread] delayedMicroTasks: $delayedMicroTasks ")

       delayedMicroTasks.each | MicroTask? m, Int j | {

         //  Mark it as null so we don't repeat it
         delayedMicroTasks[ j ] = null

         if( m is SyncMicroConfigTask ){
           syncConfigMicros.add( j )
         }
         else if( m is AutoMatchTask ) {
           autoMatchMicros.add( j )
         }
         else if( m is AutoConfigTask ) {
           autoConfigMicros.add( j )
         }
         else if( m is WireResistanceTask ) {
           Device.log.info( "[BG Thread] Adding delayed WR task for micro $j" )
           wireResistanceMicros.add( j )
         }
       }
     }
   }

}

Dan B Wed 22 Jul 2020

My solution. Was hoping for something a little more elegant

haveSync    := activeMicroTasks.any | v | { v != null && v is SyncMicroConfigTask  }
haveAC      := activeMicroTasks.any | v | { v != null && v is AutoConfigTask  }
haveAM      := activeMicroTasks.any | v | { v != null && v is AutoMatchTask  }
haveWR      := activeMicroTasks.any | v | { v != null && v is WireResistanceTask  }

//  There is active tasks.  Now check the type of active
//  versus the requested (passed in) task
if( ( ( haveSync )  && ( task isnot SyncMicroConfigTask ) )       ||
    ( ( haveAC )    && ( task isnot AutoConfigTask ) )            ||
    ( ( haveAM )    && ( task isnot AutoMatchTask ) )             ||
    ( ( haveWR )    && ( task isnot WireResistanceTask ) )        ) 
{
  canRun = false
}
else 
{
  canRun = true
}

SlimerDude Wed 22 Jul 2020

Was hoping for something a little more elegant

I dunno, that logic looks kinda complicated to me...

I mean, if you wanted to reduce the code, you could loop over the types:

taskTypes := [SyncMicroConfigTask#, AutoConfigTask#, AutoMatchTask#, WireResistanceTask#]
canRun    := !taskTypes.any |taskType| {
    activeMicroTasks.any { it != null && it.typeof.fits(taskType) } && !task.fits(taskType)
}

But then I'd argue you loose a lot of readability as to what the code actually does... time to break out the unit tests!

Dan B Wed 22 Jul 2020

Yes, I like the readability of my solution

Dan B Fri 24 Jul 2020

Actually Jeremy provided a clean and readable solution.

haveActive    := activeMicroTasks.findAll | v | { v != null }

canRun        := haveActive.any{ it.typeof.fits( task.typeof ) }

brian Fri 24 Jul 2020

In the upcoming release there is also a findNotNull method. But one method that will filter out nulls and find a specific type is the sys::List.findType method:

fansh> ["Str", null, 8, "hi"].findType(Str#)
[Str, hi]

Which I think will collapse that to one line:

canRun := !activeMicroTasks.findType(task.typeof).isEmpty

Login or Signup to reply.