So, I started working with concepts, which are awesome, and I ran across a use 
case that seems like it should work, but fails to compile. Basically, we see 
concrete types as subtypes of concepts they match in most cases, but it seems 
like they are not handled the same way when they themselves are the generic 
type. Here is some code that I think should work that doesn't compile. 
Hopefully it makes my question clear.
    
    
    import typetraits # just for the silly use of name() later
    
    type
      # A concept with a generic type
      HasId[T] = concept thing
        thing.id is T
      
      # Two types that match the concept
      IThing = object
        id: int
      
      SThing = object
        id: string
    
    let i1 = IThing(id: 0)
    let s1 = SThing(id: "hi")
    
    # We recognize the concrete objects as matching the concept
    assert i1 is HasId
    assert s1 is HasId
    
    # We recognize the types themselves as matching the concept
    assert IThing is HasId
    assert IThing is HasId[int] # explicitly naming the generic type
    assert SThing is HasId
    assert SThing is HasId[string]
    
    # We can use the concept to match concrete objects passed to procs
    proc printId(thing: HasId) : void =
      echo thing.id
    
    printId(i1)
    printId(s1)
    
    # We can use the type itself to choose which proc to match
    proc printType(thingType: typedesc[IThing]): void =
      echo name(thingType)
    
    proc printType(thingType: typedesc[SThing]): void =
      echo name(thingType)
    
    printType(IThing)
    printType(SThing)
    
    # I would like to be able to reduce those two procs above to one function 
that
    # is more flexible: that matches not against the specific object types, but
    # against the concept.
    #
    # The following will fail to compile:
    
    proc printConceptType(thingType: typedesc[HasId]): void =
      echo name(thingType)
    
    printConceptType(IThing)
    printConceptType(SThing)
    
    # I thought, maybe we should be explicit about paramterizing the generic 
type?
    # Still fails to compile:
    #
    proc printConceptTypeWithGeneric[T](thingType: typedesc[HasId[T]]): void =
      echo name(thingType)
    
    printConceptType(IThing)
    printConceptType(SThing)
    

My use case for this is I want to support a type of compile-time polymorphism, 
like generic typed marshaling with constraints. For example, I'd like to be 
able to define a `HasId` concept that requires that the object have an `id` 
field of some given type, and require a few procs to exist to manipulate that 
type. Then I want to be able to use the `system.fieldPairs` iterator to 
generically marshal and unmarshal any object that matches the `HasId` concept. 
Marshaling is pretty straightforwards, something like:
    
    
    proc marshal*(obj: HasId): SerializedType
    

but I'd like to be able to write something like:
    
    
    proc unmarshal(t: typedesc[HasId], input: SerializedType): HasId =
      result = new(t_type)
      # do stuff to de-serialize...
    

As it currently stands, I am pretty the following will work, but it seems silly 
to have to pass in a concrete object just to identify what kind of object I 
want to build. Seems more logical to pass in the type only, since I'm going to 
use reflection to build it anyway:
    
    
    proc umarshal(t: HasId, input: SerializedType): HasId =
      result = new(type(t))
      # do stuff to de-serialize...
    

Am I missing something? Is this just an edge case with concepts we haven't 
covered yet?

Reply via email to