(module forma_3_user_class_describe

  (class Describe
    (doc "A user-defined typeclass: produce a short Str describing a value.")
    (param a)
    (method describe
      (type (fn-type (params (borrow a)) (ret (own (con Str)))))))

  (data Shape
    (ctor Circle (con Int))
    (ctor Square (con Int)))

  (instance
    (class Describe)
    (type (con Int))
    (method describe
      (body
        (lam
          (params (typed n (con Int)))
          (ret (con Str))
          (body (app int_to_str n))))))

  (instance
    (class Describe)
    (type (con Shape))
    (method describe
      (body
        (lam
          (params (typed s (con Shape)))
          (ret (con Str))
          (body
            (match s
              (case (pat-ctor Circle r)
                (app int_to_str r))
              (case (pat-ctor Square w)
                (app int_to_str w))))))))

  (fn announce
    (doc "Polymorphic announcer: describe a value then print the result.")
    (type
      (forall (vars a)
        (constraints (constraint Describe a))
        (fn-type
          (params (borrow a))
          (ret (own (con Unit)))
          (effects IO))))
    (params x)
    (body (do io/print_str (app describe x))))

  (fn main
    (type (fn-type (params) (ret (own (con Unit))) (effects IO)))
    (params)
    (body
      (seq
        (seq
          (app announce 42)
          (app announce (term-ctor Shape Circle 3)))
        (app announce (term-ctor Shape Square 5))))))
