

Understanding Clojure Transducers Through Types - jkkramer
http://conscientiousprogrammer.com/blog/2014/08/07/understanding-cloure-transducers-through-types

======
rrradical
There are some interesting comments at the bottom from Rich Hickey.

------
gipp

        trans :: (b -> a) -> (c -> a -> c) -> c -> b -> c
    

in Haskell is just

    
    
        trans f reduce c = reduce c . f
    

isn't it? What am I missing here?

~~~
tel
He ends up calling that `mapping` later

    
    
        mapping :: (a -> b) -> Transducer b a
        mapping f xf r a = xf r (f a)
    

which, modulo a little renaming, eta-expansion, and point elimination is the
same as your `trans`

------
kvb
Seems like parametricity ensures that

    
    
        Transducer a b 
    

is isomorphic to

    
    
        b -> [a]
    

Am I missing something?

~~~
tel
Nope—[https://news.ycombinator.com/item?id=8160327](https://news.ycombinator.com/item?id=8160327)

------
pron
> ...a level of abstraction that I think has not been expressed much in the
> world of dynamically typed languages, although the techniques are two
> decades old in the Haskell community in a statically typed setting

Calling Clojure "dynamically typed" in this context (or any context), is
confusing, as it is more of a mashup of a few ideas from functional and OO
languages than a typical dynamically typed language. For example, Clojure does
not have dynamic dispatch (except for multimethods, which are a limited form
of dynamic dispatch) other than that offered by OO polymorphism
(interfaces/protocols). In other words, it lacks the most important mechanism
that lends dynamically typed languages like JavaScript and Ruby their power.

It is a language that, like Java/C#/Go, is based on interfaces, which are then
mixed with some functional concepts. Unlike the aforementioned OO languages,
Clojure usually uses only a handful of such abstractions (or interfaces; or
protocols). So, while the translation to Haskell requires such concepts as
type classes and higher-rank types, this has nothing to do with dynamic
typing, and a lot to do with plain old OO polymorphism.

~~~
lmkg
Clojure is dynamically typed in the sense that programs are not type-checked
at compile time, values are tagged with types, and type errors are runtime
errors. On other words, it is dynamic, in contrast to static type systems.
This says nothing about strong vs weak typing, nor expressiveness of the type
system, nor type-dependent semantics like polymorphism.

Personally I find it less confusing that dynamic typing is a small and
comparatively well-defined concept, than mixing it in with aspects of
polymorphism and dynamic dispatch. Dynamic dispatch doesn't seem to me to have
much to do with dynamic typing; it's a fundamental tool of expression in C++
and Java, and those are both statically-typed languages.

I'm curious why you think multimethods are more limited than dynamic dispatch.
It seems to me that their expressiveness is a strict superset of dynamic
dispatch. Am I missing something?

~~~
pron
> Clojure is dynamically typed in the sense that programs are not type-checked
> at compile time, values are tagged with types, and type errors are runtime
> errors.

Well, yes, but that has little to do with the way Clojure abstractions work;
namely, they're not based on dynamic dispatch, but on OO-style polymorphism.

But BTW, your definition is not entirely complete, and the distinction between
statically typed and dynamically typed is not always so clear cut when you're
not talking about extremes such as Haskell and JavaScript.

Statically typed languages like Java, C++, C# and Scala can, and do have
runtime type errors because they allow casting. They also all have type tags
(well, optional in C++). I.e., most statically typed languages don't attempt
to eliminate all type errors at compile time. Clojure indeed does very little
static type checks (I think only function arity is checked).

Even in Haskell values must be tagged with types, and the type tags are
inspected at runtime. Otherwise, pattern matching wouldn't work (pattern
matching is always based on type reflection).

> I'm curious why you think multimethods are more limited than dynamic
> dispatch.

Because methods can't (or rarely do) "appear out of nowhere" or get installed
at runtime as they do in, say, JavaScript or Groovy.

~~~
vutekst
> Even in Haskell values must be tagged with types, and the type tags are
> inspected at runtime.

This does not fit my understanding of pattern matching in Haskell. Say you are
matching over a value of type `Maybe String`. The type of the value is always
`Maybe String`, it's just that its value might be `Just "foo"` or `Nothing ::
Maybe String`. It is not to my knowledge tagged with type information at
runtime in a compiled program, merely value information. The different values
an ADT disjunction can take on all have the same type as each other.

> Otherwise, pattern matching wouldn't work (pattern matching is always based
> on type reflection).

What about pattern matching a String against a series of literal values? I
don't see how this is based on type reflection, merely inspection of values.

~~~
tel
In this context "tag" refers to the part of a value of type Maybe a which
indicates whether that actual value is of the form Just a or Nothing. In other
words, it's entirely runtime information distinguishing between various
members of the same type

~~~
vutekst
This is my understanding as well, and that's why I objected to the parent
post's assertion that "even in Haskell values must be tagged with types, and
the type tags are inspected at runtime". The enum/ADT discriminator tag is
value information rather than type information.

~~~
pron
Just and Nothing are different types even if the Haskell nomenclature doesn't
call them so. The word "type" in Haskell means something different than it
does in other languages. Haskell uses type to mean the mathematical notion of
a set, while in CS, type usually refers to data memory layout. When comparing
Haskell to other languages, we can't confuse terminology. In OO terms, for
example, Just and Nothing are subtypes of Maybe, so the tag differentiating
the two is _exactly_ the same as that used to verify downcasts from the
supertype Maybe to its subtypes, in, say, C++ or Java. Haskell implements the
very same mechanism (call it reflection or RTTI).

~~~
tel
Meh, that's a way to talk about it but the standard type system of Haskell
doesn't include subtyping like that. It's possibly you could apply a subtyping
analysis to Haskell, but it's certainly non-standard and I'm not sure what you
really, literally gain.

~~~
pron
Haskell doesn't _call_ them subtypes, but they're _implemented_ using type
tags, just like RTTI. Haskell simply uses different words to mean the same
thing (and the same words to mean different things). You can't say these
aren't type tags just because Haskell doesn't call these things types.

~~~
tel
I can say it's not a subtyping relationship because it happens entirely at
runtime and my definition of types—the one consistent with what I referred to
as Haskell's standard type analysis—does not include runtime dynamics.

If you want to pick a definition of typing which includes runtime information
(and referencing RTTI, clearly you do) then we can shift to that vocabulary
and talk about whether such an analysis includes a subtyping judgement. I'm
not familiar with it.

But it's certainly _not_ consistent with the standard Haskell type analysis.
So there shouldn't be any surprise that the vocab doesn't match up.

