Hacker News new | ask | show | jobs
by sordina 1366 days ago
One of my favourite Haskell "one-liners" is combining the AD package with Number.Symbolic:

    {-# LANGUAGE ImportQualifiedPost #-}
    
    module Module_1663406024_9206 where
    
    import Numeric.AD qualified as Ad
    import Data.Number.Symbolic qualified as Sym
    
    -- >>> f x = x^2 + 3 * x
    -- >>> Ad.diff f 1
    -- >>> Ad.diff f (Sym.var "a")
    -- 5
    -- 3+a+a

    -- >>> Ad.diff sin pi
    -- >>> Ad.diff sin (Sym.var "a")
    -- -1.0
    -- cos a
The package authors did not need to coordinate to make this possible which is pretty wild.

Saw it first here: https://twitter.com/GabriellaG439/status/647601518871359489 and https://www.reddit.com/r/haskell/comments/3r75hq/comment/cwm...

1 comments

> The package authors did not need to coordinate to make this possible which is pretty wild.

It works because `f` is polymorphic. The type of its `x` argument is not constrained in `f`'s definition, so you can plug in any `x` of any type you want provided that `x`'s type implements the methods used in `f`'s definition. With the `Dual` scheme you get to use as `x` a "dual" of `y` (`f x`, for some `f`) and `y'`, and then you get an `f` applied to that `x` where the actual `f` is parameterized by the actual `x`'s type, and so the methods called by `f` are those that apply to `x`'s type. So instead of the traditional numeric addition and multiplication, you'd get the "dual" addition and multiplication, and then everything "chains" through and you end up with `diff f x` being the `y'` in the dual of `y` and `y'` (you don't care about the `y`, just the `y'` because you want the `diff` -- the differential or derivative).

It's brilliant.

I still don't get how sin ends up as cos, without any coordination.
Presumably the Ad package has a list of known derivatives. The Sym package now "automatically" uses it, without ever having to have known of it.

The "coordination" is that they both use the "symbol" sin to refer to the idea of sine function.

Interesting that is is this way in Haskell in this example: In Julia, there are community/consensus-based processes to what the "idea" of a symbol is (or rather, the idea of a function with its current methods and its future methods defined for new types) and package interoperability often works in a similar way.
There is some confusion about what a symbol is in Julia due to bad tutorial information floating around the web. Here is Stefan Karpinski’s masterful explanation of what a Symbol really is:

https://stackoverflow.com/a/23482257

As the sibling comment mentioned, this thread started using the word "symbol" in two different ways. There is the Julia/Lisp/Ruby use of the word symbol which is related to representing code in code and to homoiconicity. That is discussed in the sibling stack-overflow link.

Earlier in this thread though "symbol" was used in a less formal, handwavy way as "a named handle to some concept in the language". In that sense, indeed, the way that Julia adds multiple methods to a single named function and supports multiple dispatch permits amazing interoperability (and has little to do with Julia/Lisp/Ruby symbol datastructures).

Right, my point is just: As new methods are added by the owners of new types it requires a common understanding and community consensus how a function should act on new types (i.e. about the "idea" of the function.)
Right, I was confused because for some reason I imagined "sin" coming from the symbolic library, but I'm assuming it's just built-in so AD knows about.
The `sin` function comes from this bit at the end of TFA:

    instance VectorSpace d => Floating (Dual d) where
      pi             = D pi zero
      exp   (D u u') = D (exp u)  (scale (exp u) u')
      log   (D u u') = D (log u)  (scale (log u) u')
  --->sin   (D u u') = D (sin u)  (scale (cos u) u')
      cos   (D u u') = D (cos u)  (scale (-sin u) u')
      sinh  (D u u') = D (sinh u) (scale (cosh u) u')
      cosh  (D u u') = D (cosh u) (scale (sinh u) u')
and the `sin` function on the right-hand side comes from `Float`, since `Float` is the type of the argument `u` in `sin u` in `D (sin u) (scale (cos u) u')`.
Not quite. This subthread is about the extremely short, one-line implementation mentioned here https://news.ycombinator.com/item?id=32882825 (which merges two unrelated modules (autodiff and symbolic) and uses autodiff to implement symbolic differentiation). Your comment is true for the original 38-line implemention of autodiff at the very top of the thread, but not in this subthread. The 38-line implementation is similar to the aforementioned autodiff module though.