OOlib icon indicating copy to clipboard operation
OOlib copied to clipboard

`isInstanceOf` problem with multiple `impl`

Open griffith1deady opened this issue 1 year ago • 4 comments

Hello! I'm glad you added this feature, but currently this work only for this case:

import oolib

protocol Launchable:
    proc prepareForLaunch()
    proc launch()
    proc stop()

protocol HotCompilable:
    proc configureReloading()

class ServerLaunchable impl (Launchable):
    proc prepareForLaunch = discard
    proc launch = discard
    proc stop = discard
    proc configureReloading = discard

let serverLaunchable = ServerLaunchable.new()
echo serverLaunchable.isInstanceOf(Launchable) #true
echo serverLaunchable.isInstanceOf(HotCompilable) #false

and not for this:

import oolib

protocol Launchable:
    proc prepareForLaunch()
    proc launch()
    proc stop()

protocol HotCompilable:
    proc configureReloading()

class ServerLaunchable impl (Launchable, HotCompilable):
    proc prepareForLaunch = discard
    proc launch = discard
    proc stop = discard
    proc configureReloading = discard

let serverLaunchable = ServerLaunchable.new()
echo serverLaunchable.isInstanceOf(Launchable) #false
echo serverLaunchable.isInstanceOf(HotCompilable) #false

griffith1deady avatar Mar 07 '24 21:03 griffith1deady

Tryed rewrite code for checking like this:

macro isInstanceOf*(value: typed, protocol: typed): bool =
  let protocolDefinition = if ProtocolTable.hasKey(protocol.strVal):
    protocol
  else:
    newEmptyNode()

  if protocolDefinition.kind == nnkEmpty: return newLit(false)
  
  let protocolImpl = getImpl(protocol)

  for protocolFieldIndex, protocolFieldNode in protocolImpl[2]:
      let protocolParameterName = if protocolFieldNode.kind == nnkIdent: protocolFieldNode[0].strVal else: repr(protocolFieldNode[0])
      let protocolParameterType = protocolFieldNode[1]

      let destinationImpl = if value.kind == nnkObjConstr: getImpl(value[0]) else: getImpl(getImpl(value)[2][1])
      var isValid = false

      for destinationFieldIndex, destinationFieldNode in destinationImpl[2][0][2]:
        let parameterName = if destinationFieldNode.kind == nnkIdent: destinationFieldNode[0].strVal else: repr(destinationFieldNode[0])
        let parameterType = destinationFieldNode[1]

        isValid = isValid or (protocolParameterName == parameterName) and (protocolParameterType == parameterType)

      result = newLit(isValid)

but seem's like procedures not stored like destination proc fields, and then from your side need make CacheTable like [Destination, seq[proc]], where destination - generated type for protocol impl, and seq[proc] is implemented procedures

griffith1deady avatar Mar 08 '24 08:03 griffith1deady

For example, my idea works with that code:

macro isInstanceOf*(value: typed, protocol: typed): bool =
  let protocolDefinition = if ProtocolTable.hasKey(protocol.strVal):
    protocol
  else:
    newEmptyNode()

  if protocolDefinition.kind == nnkEmpty: return newLit(false)
  
  let protocolImpl = getImpl(protocol)
  var isValidDestination = false

  for protocolFieldIndex, protocolFieldNode in protocolImpl[2]:
      let protocolParameterName = if protocolFieldNode.kind == nnkIdent: protocolFieldNode[0].strVal else: repr(protocolFieldNode[0])
      let protocolParameterType = protocolFieldNode[1]

      let destinationImpl = if value.kind == nnkObjConstr: getImpl(value[0]) else: getImpl(getImpl(value)[2][1])
      for destinationFieldIndex, destinationFieldNode in destinationImpl[2][0][2]:
        let parameterName = if destinationFieldNode.kind == nnkIdent: destinationFieldNode[0].strVal else: repr(destinationFieldNode[0])
        let parameterType = destinationFieldNode[1]

        isValidDestination = isValidDestination or (protocolParameterName == parameterName) and (protocolParameterType == parameterType)

  if result.kind == nnkEmpty:
    result = newLit(isValidDestination)
  elif result.kind == nnkSym:
    let storedValue = result.boolVal
    if storedValue and not isValidDestination:
      return newLit(false)

and then testing code:

import oolib

protocol Launchable:
    proc prepareForLaunch()
    proc launch()
    proc stop()

protocol HotCompilable:
    proc configureReloading()

protocol Understable:
    proc idk()

class ServerLaunchable impl (Launchable, HotCompilable):
    proc prepareForLaunch = discard
    proc launch = discard
    proc stop = discard
    proc configureReloading = discard

type IDK = ref object
    configureReloading: proc()
    idk: proc()

let serverLaunchable = ServerLaunchable.new()

echo IDK().isInstanceOf(HotCompilable) #true
echo IDK().isInstanceOf(Understable) #true
echo IDK().isInstanceOf(Launchable) #false
echo serverLaunchable.isInstanceOf(Launchable) #false
echo serverLaunchable.isInstanceOf(HotCompilable) #false
echo serverLaunchable.isInstanceOf(Understable) #false
echo serverLaunchable.isInstanceOf(string) #false 

griffith1deady avatar Mar 08 '24 08:03 griffith1deady

Thank you for letting me know this issue, I've completely forgotten about multiple implementation. I was unsure of how to proceed, but I now realize making protocols with custom pragma can be an easier solution. This will allow isInstanceOf to read their names to check whether an instance matches a type.

protocol P:
  ...

protocol P2:

class C impl (P, P2):
  ...

# will be converted to below

type P = tuple
  ...

type C {.derive: ["P", "P2"].} = ref object
  ...

glassesneo avatar Mar 11 '24 09:03 glassesneo

What's the status about this?

griffith1deady avatar Mar 19 '24 18:03 griffith1deady

My apologies for my late response. I had been struggling with my own problems. Would you check if isInstanceOf() works properly?

glassesneo avatar Jun 01 '24 14:06 glassesneo