shamazmazum / cl-forward-diff

Automatic differentiation for Common Lisp (forward mode)
BSD 2-Clause "Simplified" License
7 stars 0 forks source link

Using coalton #1

Open digikar99 opened 1 day ago

digikar99 commented 1 day ago

I discovered cl-forward-diff from your message on sbcl-help and wondered if you had checked out coalton. We are actually in the last stages of adding inlining support, but once it's done, the parametric typing as well as the type inference machinery + much better function type support should all be available for performant (portable?) numerical computation!

shamazmazum commented 1 day ago

Hi! Coalton even has its own Dual type, as it seems. If I write something like this:

(defpackage foo
  (:use :coalton :coalton-prelude)
  (:local-nicknames (:math :coalton-library/math))
  (:export :foo :bar))
(in-package :foo)

(coalton-toplevel
  (declare foo (Num :a => math:Dual :a -> math:Dual :a -> math:Dual :a))
  (define (foo x y)
    (* x (1+ y)))

  (declare bar (Num :a => :a -> :a -> Tuple :a :a))
  (define (bar x y)
    (Tuple
     (math:dual-part
      (foo (math:Dual x 1) (math:Dual y 0)))
     (math:dual-part
      (foo (math:Dual x 0) (math:Dual y 1))))))

can I call bar from lisp? Something like

(in-package :cl-user)
(defun bar (x y)
  (coalton:coalton (bar x y)))

I Cannot figure it out by skimming the documentation.

Currently, I don't have any opinion on Coalton yet. At first, I think that using Coalton in projects which I share here on Github would be impractical, because it must be manually loaded which I think is annoying. Secondly, I think, it lacks tooling. E.g. when I press M-. on foo in slime for the example I provided, it goes to coalton-toplevel instead of the definition of foo. It seems that integration with slime is worse for Coalton than for CL. Also Coalton reminds me of Haskell, but without laziness, and I don't know if I want to prefer the former over the latter. But anyway, it seems like a nice language, I am just not sure how it can be useful for me personally.

digikar99 commented 5 hours ago

My current understanding is that after you have the generic coalton functions, you monomorphize the ones you want to use from standard CL.

(coalton-toplevel
  (declare bar-single-float
           (single-float -> single-float -> Tuple single-float single-float))
  (monomorphize)
  (define (bar-single-float x y)
    (bar x y)))

And then, you can compile the calls to these from standard CL or use (coalton (<fn> ... <args> ...)) at the REPL:

(defun bar ()
  (coalton:coalton (foo::bar 1.0 2.0)))

I think it is fine to use a lisp image to automate the loading of coalton. About the tooling, there has been recent progress on both adding a coalton mode to emacs as well as source tracking. I myself am yet to try any of these.

For me, the primary attraction to coalton stems from being able to write optimizable extensible generic code. In standard CL, you can either write generic code which will be slow. Even if you make it fast using static-dispatch or fast-generic-functions, it remains non-extensible. Sticking with SBCL is definitely another alternative, that seems more-or-less enough to write optimizable extensible generic code. But, if you dive into higher order functions or passing around functions, one again runs into a loss :/, unless perhaps one uses MOP-fu to provide typed functions.

I still definitely dislike having to juggle between lisp and coalton and would have loved a more seamless integration, but standard CL seems like a dead-end in itself for these kinds of tasks.

And I see, if you value laziness, coalton might not be the good choice. But I could be mistaken, I myself am only barely familiar with coalton! There is also the coalton discord server!