.NET
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

ObjectId in F# "The type does not support the 'comparison' constraint"

7 REPLIES 7
SOLVED
Reply
Message 1 of 8
dunnamin
2474 Views, 7 Replies

ObjectId in F# "The type does not support the 'comparison' constraint"

Howdy,

 

I would like to use the F# collection types on ObjectIds.  When I try with some, I get an error saying the type does not support the 'comparison' constraint, for example, it does not support the System.IComparable interface.  I can use it with a dictionary, but that is mutable and I would also like to use it in Sets and such.

 

Here is some code only included to show the declarations that won't work. The first declaration is along the lines of what I need, in that it uses a Map instead of a Dictionary and an F# Set instead of the built-in ObjectIdCollection (unless the ObjectIdCollection has things like 'choose', etc..

 

I'm not sure if I can use some attributes to make ObjectIds available to the F# collections or if I need to extend the class, but either would be preferable to recreating the functionality of a Set, as I find myself doing now.  Does anyone know how to use an ObjectId in an F# Map, Set, etc.?

 

AutoCAD 2015, Visual Studio 2013

 

namespace CompareQ

open Autodesk.AutoCAD.DatabaseServices
open System
open System.Collections.Generic

module LinkedObjectIds =

    let foo (i : int) (m : Map<int, Set<int>>) =
        let bar = Map.tryFind i m
        match bar with
        | None -> Set.empty
        | Some x -> x

    let bar (o : ObjectId)
             (d : Dictionary<ObjectId, ObjectIdCollection>) =
        let found, os = d.TryGetValue(o)
        if found then
            Some os
        else
            None

    let baz (o : ObjectId) (m : Map<ObjectId, Set<ObjectId>>) =
        let bar = Map.tryFind o m
        match bar with
        | None -> Set.empty
        | Some x -> x

    let boz (o : ObjectId) (m : Map<ObjectId, ObjectIdCollection>) =
        let bar = Map.tryFind o m
        match bar with
        | None -> Set.empty
        | Some x -> x

 

7 REPLIES 7
Message 2 of 8
dunnamin
in reply to: dunnamin

In the above code, 'foo' and 'bar' get past the syntax/type checker, 'baz' and 'boz' do not.  I would like to use something like 'baz'.  (With, of course, passing along an environment variable containing a map.)

 

A strange thing is that ObjectId containes Compare, CompareTo, Equals, and also operators '==', '!=', '<' and '>', but does not apparently use the IComparable interface?

 

Thanks,

   -- dunn

Message 3 of 8
_gile
in reply to: dunnamin

Hi,

 

The ObjectId structure implements the generic IComparable<ObjectId> interface but does not implement IComparable which is the required interface for Set or Map collections.

 

You can create a new type using an underlying ObjectId which implement IComparable.

 

type ComparableId (oid: ObjectId) =
    member x.ObjectId = oid
    member x.OldIdPtr = oid.OldIdPtr

    override x.Equals(obj: obj) =
        obj :? ComparableId && x.ObjectId = (unbox<ComparableId> obj).ObjectId 

    override x.GetHashCode() = x.ObjectId.GetHashCode()

    interface IComparable<ComparableId> with
        member x.CompareTo(other: ComparableId) = 
            compare x.OldIdPtr other.OldIdPtr

    interface IComparable with
        member x.CompareTo(obj: obj) = 
            match obj with
            | :? ComparableId -> compare x.OldIdPtr (unbox<ComparableId> obj).OldIdPtr
            | _ -> invalidArg "obj" "Must be of type ComparableId"
        
let foo (id : ObjectId) (m : Map<ComparableId, Set<ComparableId>>) =
    match Map.tryFind (ComparableId id) m with
    | Some x -> x |> Seq.map (fun i -> i.ObjectId)
    | None -> Seq.empty

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 4 of 8
dunnamin
in reply to: _gile

Thank you so much, Gilles!  Not being able to use ObjectId where F# needed Comparable was a huge stumbling block for me.  I was busily rewriting the functionality of Map and Set with lists, all the while knowing they were not as robust or efficient as built-in libraries.  I am very grateful to you for taking the time and making the effort to help!

 

   -- dunn

Message 5 of 8
_gile
in reply to: dunnamin

I'm glad if I gave some help.

We're not so much palying F# with AutoCAD.

I'm not sure the way I purposed is the best one, but it's the first that came to my mind as extension methods can't add interface inheritance to the extended type (structure or class).



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 6 of 8
dunnamin
in reply to: _gile

Howdy again!

 

I have been trying to debug what I am trying to do, and it kept crashing.  I think the error is in this conversion of ObjectId to use IComparable so I can have immutable data structures.

 

I have tried to remove all other code to recreate the problem with as few lines as possible.  The code for ComparableId has the Id renamed to ObjId, but other than that is what was posted earlier by Gilles in answer to my first post (thanks, Gilles!).

 

This seems to go into an infinite loop at the "Interface IComparable with" function.

 

namespace ObjTest

module ComparableId =
    open Autodesk.AutoCAD.DatabaseServices
    open System

    type ObjId (oid: ObjectId) =
        member x.ObjectId = oid

        override x.Equals(obj: obj) =
            obj :? ObjId && x.ObjectId = (unbox<ObjId> obj).ObjectId 

        override x.GetHashCode() = x.ObjectId.GetHashCode()

        interface IComparable<ObjId> with
            member x.CompareTo(other: ObjId) = 
                compare x.ObjectId.OldIdPtr other.ObjectId.OldIdPtr

        interface IComparable with
            member x.CompareTo(obj: obj) = 
                match obj with
                | :? ObjId -> compare x (unbox<ObjId> obj)
                //| :? ObjId as o -> compare x o
                | _ -> invalidArg "obj" "Must be of type ObjId"
            
    let foo (id : ObjectId) (m : Map<ObjId, Set<ObjId>>) =
        match Map.tryFind (ObjId id) m with
        | Some x -> x |> Seq.map (fun i -> i.ObjectId)
        | None -> Seq.empty

module LinkManager =
    open Autodesk.AutoCAD.ApplicationServices
    open Autodesk.AutoCAD.Runtime
    open Autodesk.AutoCAD.DatabaseServices
    open Autodesk.AutoCAD.EditorInput
    open Autodesk.AutoCAD.Geometry
    open System
    open Option
    open ComparableId

    // Linkmap is not used in these declarations,
    //  but is the map being passed around to the two functions
    type Linkmap = Map<ObjId, Set<ObjId>>

    // Add unidirectional link to Linkmap
    let linkId (src : ObjId) (dst : ObjId) linkmap =
        let dsts = Map.tryFind src linkmap
        match dsts with
        | Some dsts -> Map.add src (Set.add dst dsts) linkmap
        | None -> Map.add src (Set.singleton dst) linkmap

    // Add bidirectional link to passed linkmap
    let linkIds id1 id2 =
           linkId id1 id2
        >> linkId id2 id1

    [<CommandMethod("OBJTEST")>]
    let linkEntities() =
        let doc = Application.DocumentManager.MdiActiveDocument
        let db = doc.Database
        let ed = doc.Editor

        let opts = PromptEntityOptions("\nSelect first circle to link: ")
        opts.AllowNone <- true
        opts.SetRejectMessage("\nOnly circles can be selected.")
        opts.AddAllowedClass(typeof<Circle>, false)

        let res = ed.GetEntity(opts)
        if res.Status = PromptStatus.OK then
            let src=res.ObjectId
            opts.Message <- "\nSelect circle to link: "
            let res = ed.GetEntity(opts)
            if res.Status = PromptStatus.OK then
                let dst = res.ObjectId
                linkIds (ObjId src) (ObjId dst) Map.empty |> ignore

 This is quite a show-stopper for me, since the immutability of functional programming is needed.  The ComparableId allows me to put ObjectIds into maps and sets, but I cannot seem to use them yet.

 

Does anyone know where I'm going wrong?

 

Thanks,

   -- dunn

Message 7 of 8
_gile
in reply to: dunnamin

Hi,

 

You probably copied the code before I edited it.

In the IComparable implementation, you have to compare the ObjectId.OldIdPtr too.

 

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 8 of 8
dunnamin
in reply to: _gile

Thanks again, Gilles!

 

I got it, and it works great.  I really appreciate your help 🙂

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk DevCon in Munich May 28-29th


Autodesk Design & Make Report

”Boost