61

One thing I like very much is reading about different programming languages. Currently, I'm learning Scala but that doesn't mean I'm not interested in Groovy, Clojure, Python, and many others. All these languages have a unique look and feel and some characteristic features. In the case of Clojure I don't understand one of these design decisions. As far as I know, Clojure puts great emphasis on its functional paradigm and pretty much forces you to use immutable "variables" wherever possible. So if half of your values are immutable, why is the language dynamically typed?

The Clojure website says:

First and foremost, Clojure is dynamic. That means that a Clojure program is not just something you compile and run, but something with which you can interact.

Well, that sounds completely strange. If a program is compiled you can't change it anymore. Sure you can "interact" with it, that's what UIs are used for but the website certainly doesn't mean a neat "dynamic" GUI.

How does Clojure benefit from dynamical typing

I mean the special case of Clojure and not general advantages of dynamic typing.

How does the dynamic type system help improve functional programming

Again, I know the pleasure of not spilling "int a;" all over the source code but type inference can ease a lot of the pain. Therefore I would just like to know how dynamic typing supports the concepts of a functional language.

Krzysztof Atłasik
  • 21,985
  • 6
  • 54
  • 76
lhk
  • 27,458
  • 30
  • 122
  • 201
  • 7
    Remember that Clojure is a Lisp, and Lisps have been dymanically typed since forever, with very few exceptions (e.g. Typed Racket) - which never got much attention anyway. –  Feb 24 '11 at 20:54
  • 12
    Immutability of the **value** of variables, and Dynamic **typing** of variables, are two very different concepts...not sure I get your first paragraph. – Chris Pfohl Feb 24 '11 at 21:01
  • 1
    I've programmed in Python and Javascript as well as in Pascal, Java, C++ and C#. I certainly know the differences between static and dynamic typing. Up to know I don't have any preferences, to me only the language itself matters. Therefore I would like to know why the designers of clojure chose dynamic over static. The strange statement on their website confused me and now I'd like to know the advantages of dynamic typing for clojure (not in general) and for the functional programming paradigm in general. – lhk Feb 27 '11 at 19:29
  • 3
    I think you misread that quote from website. It says that "Clojure is dynamic" not that Clojure is dynamically typed (although it is). That is different emphasis. It says that most things in Clojure are reified and can be changed at runtime (e.g. namespaces). This is pragmatic choice and no one objects if you add-on some type restrictions (see e.g. type hints). Rich Hickey noted that he would want type system/restrictions to be pluggable, that is, be orthogonal to other language design choices. And this sounds sensible for me. – Petr Gladkikh Jul 10 '11 at 05:23
  • You can plug in your own type system in Lisp which is what clojure.typed and TypedRacket is. I am trying to research how to convert unit tests i.e: (=(myadd a b) (+ a b)) into a type annotations for clojure.typed using type inference. Lisp is so dynamic that having a types that works for all code is impossible, but macros run at compile time allowing static analysis. Hickey gave the example in Haskell if you convert a function from (Maybe -> a) or (a -> Maybe a) you have to write fromJust Just in huge parts of code (although the compiler will tell you where, and it can be automatic). – aoeu256 Jul 17 '19 at 14:05

5 Answers5

26

If a program is compiled you can't change it anymore.

This is wrong. In image-based systems, like Lisp (Clojure can be seen as a Lisp dialect) and Smalltalk, you can change the compiled environment. Development in such a language typically means working on a running system, adding and changing function definitions, macro definitions, parameters etc. (adding means compiling and loading into the image).

This has a lot of benefits. For one, all the tools can interact directly with the program and do not need to guess at the system's behaviour. You also do not have any long compilation pauses, because each compiled unit is very small (it is very rare to recompile everything). The NASA JPL once corrected a running Lisp system on a probe hundreds of thousands of kilometres away in space.

For such a system, it is very natural to have type information available at runtime (that is what dynamic typing means). Of course, nothing hinders you from also doing type inference and type checks at compilation time. These concepts are orthogonal. Modern Lisp implementations typically can do both.

Svante
  • 50,694
  • 11
  • 78
  • 122
  • 10
    What's an image based system ? – lhk Feb 26 '11 at 16:33
  • @lhk See [here](http://stackoverflow.com/questions/480083/). It's basically a system that lets you dump it in it's current state into an image, which you can use for whatever purpose (e.g. make an executable: dump the system into an executable that loads it + runs the entry point). – MasterMastic May 27 '16 at 09:59
  • @Svante, the concepts are not orthogonal. Dynamic typing goes beyond allowing type read at runtime—the type can be changed (ie adding and subtracting methods). A statically typed language turns runtime errors into compile time errors. – Pacerier Jan 15 '22 at 00:52
  • A _type_ doesn't have _methods_. A _type_ is just a set of values. But anyway, yes, there are further implications and tradeoffs. For me, disallowing small changes, always re-compiling the world is a pretty big drawback. And finally even in e. g. Idris, you still have to _use_ the type system well, which is not trivial, but anything short of perfection could benefit a lot from graceful runtime behaviour in the face of errors. – Svante Jan 15 '22 at 12:16
19

Well first of all Clojure is a Lisp and Lisps traditionally have always been dynamically typed.

Second as the excerpt you quoted said Clojure is a dynamic language. This means, among other things, that you can define new functions at runtime, evaluate arbitrary code at runtime and so on. All of these things are hard or impossible to do in statically typed languages (without plastering casts all over the place).

Another reason is that macros might complicate debugging type errors immensely. I imagine that generating meaningful error messages for type errors produced by macro-generated code would be quite a task for the compiler.

sepp2k
  • 363,768
  • 54
  • 674
  • 675
  • I didn't know evaluating arbitrary code is possible in a compiled language. That explains "interactive". Beside that I care less for the poor compiler than for the person meant to do the debugging ;-) – lhk Feb 24 '11 at 21:12
  • 1
    @lhk: My point was that since it would be quite a task for the compiler, it would be very likely to produce sub-par results - to the dismay of the poor person doing the debugging. Just think of template error messages in C++ *shudder* – sepp2k Feb 24 '11 at 21:14
  • Re: "All of these things are hard or impossible to do in statically typed languages (without plastering casts all over the place)." This may be true of certain *implementations* of many statically-typed languages; however, such a limitation is not dictated by the constraints of statically-typed languages. – David J. Dec 05 '20 at 19:13
  • Re: "define new functions at runtime". A statically typed language could choose to allow the definition of functions at runtime. This could happen in a REPL, for example. – David J. Dec 05 '20 at 19:19
  • To be clear, there is a difference between: (1) a language having a static type system; and (2) an implementation of a language using compilation. Note the distinction between a *language* and an *implementation*. – David J. Dec 05 '20 at 19:21
  • Yes, most statically typed languages (at least ones that people think of and/or have invented so far) correspond to compiled implementations, but the underlying concepts are different. – David J. Dec 05 '20 at 19:23
  • @DavidJ.I wasn't talking about defining a function in a REPL. That's not what I meant by "at runtime". When you do that, the function definition will be type-checked after you enter the function definition and then the method call will be type-checked after you enter the method call (at which point the type of the method will be known). That's fine... – sepp2k Dec 05 '20 at 19:39
  • ... But in a regular program in a statically typed language, the whole program is type-checked before any of it runs - that's what "statically typed" means. So if you can't statically determine whether a function is defined (or what its name is), then how can you statically type-check calls to that function? – sepp2k Dec 05 '20 at 19:39
  • 1
    And just to be clear: Whether you need casts to do something or whether a given piece of code is legal or not is a property of the language, not the implementation. If the rules of a language says that `print(eval("12 + 13") * 2)` is legal, then all implementations of the language must accept that code or they're not valid implementations of that language. And if the rules say that that code is ill-typed unless you add a cast, then all implementations must require a cast. – sepp2k Dec 05 '20 at 19:51
15

I agree, a purely functional language can still have an interactive read-eval-print-loop, and would have an easier time with type inference. I assume Clojure wanted to attract lisp programmers by being "lisp for the jvm", and chose to be dynamic like other lisps. Another factor is that type systems need to be designed as the very first step of the language, and it's faster for language implementors to just skip that step.

Tobu
  • 24,771
  • 4
  • 91
  • 98
  • 4
    Being a Lisp might have been part of it, but Rich Hickey often promotes dynamic typing as having a lot of virtues in it's own right, and might have made this decision even if he hadn't chosen to make Clojure a Lisp. – metasoarous Jan 10 '15 at 05:02
  • 11
    “[T]ype systems need to be designed as the very first step of the language” — Not so. [Typed Racket](http://docs.racket-lang.org/ts-guide/) builds a type system on top of an untyped functional language (Racket). – Matthew Butterick Jan 26 '15 at 03:21
  • 5
    The same thing was done with [Typed Clojure](http://typedclojure.org/) — you can even mix static and dynamically typed code. – Zaz Feb 11 '15 at 07:29
  • Although Haskell(hard language for a the Pythonista in me) defaults to static typing with type inference, you can also change it to be dynamic using http://hackage.haskell.org/package/dynamic. You can also set it to ignore type errors, and Template Haskell can do what Lisp macros do but its much much harder at first, but once your done you do get static type guarantees (I guess this is like the rest of Haskell...). The benefits of static typing allow you to use hoogle and _ (holes) to help you write your code for you, sort of like "general autocompletion". – aoeu256 Aug 06 '19 at 14:12
7

(I'm rephrasing the original answer since it generated too much misunderstanding)

One of the reasons to keep Clojure (and any Lisp) dynamically typed is to simplify creation of macros. In short, macros deal with abstract syntax trees (ASTs) which can contain nodes of many, many different types (usually, any objects at all). In theory, it's possible to make full statically typed macro system, but in practice such systems are usually limited and sparsely spread. Please, see examples below and extended discussion in the thread.


EDIT 2020: Wow, 9 years passed from the time I posted this answer, and people still add comments. What a legacy we all have left!

Some people noted in comments that having a statically typed language doesn't prevent you from expressing code as data structure. And, strictly speaking, it's true - union types allow to express data structures of any complexity, including syntax of a language. However I claim that to express the syntax, you must either reduce expressiveness, or use such wide unions that you lose all advantages of static typing. To prove this claim I will use another language - Julia.

Julia is optionally typed - you can constrain any function or struct field to have a particular type, and Julia will check it. The language supports AST as a first class citizen using Expr and Symbol types. Expression definition looks something like this:

struct Expr
  head::Symbol
  args::Vector{Any}
end

Expression consists of a head which is always a symbol and list of arguments which may have any types. Julia also supports special Union which can constrain argument to specific types, e.g. Symbols and other Exprs:

struct Expr
  head::Symbol
  args::Vector{Union{Symbol, Expr}}
end

Which is sufficient to express e.g. :(x + y):

dump(:(x + y))
Expr
  head: Symbol call
  args: Array{Any}((3,))
    1: Symbol +
    2: Symbol x
    3: Symbol y

But Julia also supports a number of other types in expressions. One obvious and helpful example is literals:

:(x + 1)

Moreover, you can use interpolation or construct expressions manually to put any object to AST:

obj = create_some_object()

ex1 = :(x + $objs)
ex2 = Expr(:+, :x, obj)

These examples are not just a funny experiments, they are actively used in real code, especially in macros. So you cannot constrain expression arguments to a specific union of types - expressions may contain any values.

Of course, when designing a new language you can put any restrictions on it. Perhaps, restricting Expr to contain only Symbol, Expr and some Literals would be useful in some contexts. But it goes against principles of simplicity and flexibility in both - Julia and Clojure, and would significantly reduce usefulness of macros.

ffriend
  • 27,562
  • 13
  • 91
  • 132
  • 22
    It would be a `'a -> 'b`, with `'a` and `'b` further restricted by the type of the `(*)` operator. – Tobu Feb 24 '11 at 21:09
  • How about `List` where a `SyntaxElement` is either a symbol, a `Vector` or another `List`? Well, I suppose map literals and other syntax constructs would have to be taken into account as well, but it's certainly not impossible to type this. – sepp2k Feb 24 '11 at 21:09
  • 2
    @Tobu: That would be the type `square` after `(defn square [x] (* x x))` was evaluated, but I don't think that was ffriends point. His point, as I understood it, was what the type would be if you pass the function definition around *as data*. – sepp2k Feb 24 '11 at 21:10
  • 6
    @Tobu: I'm asking not about result type, but about type of the expression as a list of symbols. I.e. what is the type of `(list 'defn 'square ['x] '(* x x))` – ffriend Feb 24 '11 at 21:12
  • 3
    Btw: As I understand them type hints are meant as a way to improve performance, not to guarantee type safety in any way. – sepp2k Feb 24 '11 at 21:12
  • @sepp2k: Your `SyntaxElement` may be anything, any `Object`, no type restriction here. So, you get same dynamic typing, but with the need of multiple coercing everywhere. As for type safety of type hints, yeah, they really do not prevent from all type errors at the moment, but AFAIK Rich Hickey is going to improve them to do it. – ffriend Feb 24 '11 at 21:18
  • @ffriend: No, it couldn't be anything that can't be expressed as a literal. And I was thinking more along the lines of a tagged union, so you wouldn't really have to coerce anything, just match on the type which you have to do anyway. Though, yes, it would be a bit more verbose. – sepp2k Feb 24 '11 at 21:22
  • Ah, I missed that it shouldn't be evaluated. To get the ability to prevent evaluation and play with syntax trees, you can add quotations and antiquotations to the language. – Tobu Feb 24 '11 at 21:28
  • @sepp2k: `(def obj (SomeComplexObject. )) (defmacro some-macro [] (list 'some-func obj)) (some-macro)`. `obj` is evaluated at a time of compilation of macro. With this in mind, what is the type of last expression? `obj` already doesn't belong to your union. – ffriend Feb 24 '11 at 21:58
  • @ffriend: Yes, but `obj` isn't passed as a parameter to `some-macro` in that code, so it's type does not have to be `SyntaxElement`. There's no problem with its type being `SomeComplexObject`. – sepp2k Feb 24 '11 at 22:10
  • 1
    Ooops, it won't work. Nevertheless, my point is that Lisp has many stages of compilation and running, and during them you can use objects of many types. Static typing will introduce a lot of complexity, so carrying about types will make writing macro code a hell. – ffriend Feb 24 '11 at 22:12
  • @Tobu: To play with syntax trees safely you will still have to keep type info on expressions, even in AST. Quoting and unquoting do nothing with static typing by themselves. – ffriend Feb 24 '11 at 22:41
  • @sepp2k: Ok, but why do you think `SyntaxElement` by itself guarantees type safety? If it is a `Symbol`, it may be evaluated during macro expansion to anything. Consider such an example: `(defmacro include-in-code-if [condition expr] (if condition '(do '~expr)))`. – ffriend Feb 24 '11 at 22:46
  • @ffriend: If it is a symbol, it will just evaluate to itself (during macro expansion). If it is a list, it can evaluate to anything. And yes, that will let you call a macro with `SyntaxElement`s that evaluate to the wrong type. But any type error this will cause, will still happen at compile time. So you still have your type safety; it's just that you only have it after macro expansion. Doing real type-safe macros would be a lot more complicated (but still possible - just maybe too unwieldy to be useful) - all I'm saying is that it's possible to type this statically, not that it's a good idea. – sepp2k Feb 24 '11 at 23:03
  • @sepp2k: why do you think, that symbol will evaluate to itself? Look at my `include-in-code-if` example - symbol `condition` evaluates to boolean type. So, `condition` isn't a part of any union, it is just an `Object`. – ffriend Feb 25 '11 at 00:00
  • @ffriend: `condition` does not evaluate to a boolean type unless you actually pass a boolean literal in. If you do something like `(def x false) (include-in-code-if x (foo))`, `condition` in `include-in-code-if` will be the symbol `x` (and thus true), not the boolean value false. – sepp2k Feb 25 '11 at 00:20
  • @sepp2k: if you pass symbols like `x`, you can still `eval` them and get object of any type, can't you? – ffriend Feb 25 '11 at 00:51
  • @ffriend: Well, yes, but then the problem is that `eval` can't be usefully typed, not that macros can't be. – sepp2k Feb 25 '11 at 02:14
  • 1
    @sepp2k: I tried to show, that Lisp mixes macroexpand, compile and run time in all possible ways. During all of these stages you can work with symbols, that can evaluate to anything. This is the core metaprogramming feature of Lisp, feature, that makes code-as-data philosophy. You cannot introduce `SyntaxElement` in macros, but drop it at run time. Moreover, explicit `eval` is needed only on macroexpansion time, compile and run time use it implicitly. Another problem comes from `apply`, and with `eval-apply` loop using static typing is much, much harder, then just introducing `SyntaxElement`. – ffriend Feb 25 '11 at 10:31
  • @ffriend: `eval` (and to some extend `apply` if you don't accept making it take a tuple instead of a list as a valid solution) is not usefully typeable - we don't disagree on that. However the problem is not the fact that Clojure has macros, if Clojure didn't have macros, we'd still have the same problem. And it's very well possible to create a language with lisp-like macros, but without `eval` which can work well as a statically typed language. And I didn't say `SyntaxElement` should only exist at compile-time. `quote` would return syntax elements as well. – sepp2k Feb 25 '11 at 10:42
  • 1
    @ffriend: Though honestly, we should stop arguing about this. On the basic conclusion that Clojure wouldn't work well as a statically typed language we agree - the rest is just nitpicking on my part (but only because I firmly believe in useful macro systems for statically typed languages). – sepp2k Feb 25 '11 at 10:43
  • 1
    @sepp2k: I believe, combining lispy macro system and static typing is quite interesting topic, nevertheless, I agree, that it is not the place to make a long conversation. I only want to make a little final note to explain my position along all the comments: I do not think, that Lisp macro system is restricted to `defmacro`, it is more about symbolic programming with _all its power_. So, the question is how to combine processing symbols (quoting, evaluating, applying, etc) and static types. – ffriend Feb 25 '11 at 15:48
  • Its anything that takes the * operator, and it could be statically checked with type inference. Maybe, someone will add a type inference mode, but you need a basic annotations to use with inference (clojure.typed is gradual typing which requires explicit type annotations). – aoeu256 Jul 17 '19 at 14:19
  • Poster shows inexperience with generic types. Just about anything in a dynamically typed language can be typed. Typing is not a property of languages it is a property of reality. If the types are incompatible the function will crash it doesn't matter if the language is typed or not. What is missing from closure is pre-compile time type checking. The type checking you are looking for for your specific example is actually available in all major programming languages. Look up generics. – Brian Yeh Oct 15 '20 at 02:41
  • @BrianYeh Perhaps you mean union types, not generics? I edited the post to emphasize restrictions of static typing on ASTs. – ffriend Oct 15 '20 at 23:34
  • @ffriend Same thing. A generic type on the multiplication operator (*) represents a union of all possible types that can be used with the multiplication operator. See example: ```function add (x: a, y: a) -> a { return y * x }``` where the type 'a' represents a genetic type that can be multiplied OR a union of all types that can be multiplied. Either perspective is correct. – Brian Yeh Oct 16 '20 at 00:59
  • @BrianYeh Please, read previous discussion, especially [this comment](https://stackoverflow.com/questions/5110292/why-is-clojure-dynamically-typed/5110459?noredirect=1#comment5729969_5110459). Both - in the answer and in comments - I tell and emphasize that the provided code snippet should be interpreted as a _list_ of symbols and expressions, and further explain it in detail with examples in another language. Not sure why you still try to talk about generic types, which are totally irrelevant here. – ffriend Oct 16 '20 at 07:43
  • @ffriend. The type signature is this `(a, a) -> a` which is a function that takes two types of type `a` and returns the same type `a`. This means you cannot have `(a, b) -> c` the return type must equal the parameter types, this is called parametric polymorphism. What is `a`? `a` is a type variable that can represent any type. In most typed programming language you can choose to restrict `a` to be any type that can use the `*` operator. So `a` can include the `list` type or the `int` type or the `float` type. – Brian Yeh Oct 17 '20 at 08:12
  • @ffriend Your original function is already typed with this signature: `(a, a) -> a` with `a` equal to any type that uses the `*` operator because if you use any other type `a` that does not use the `*` you will get an error. The only difference between an untyped language and a typed language is that in the typed language can allow you to specify the type signature explicitly and the compiler can catch the error before the program runs. In untyped languages the error can only be caught while the program is running. That's it. – Brian Yeh Oct 17 '20 at 08:15
  • @BrianYeh Are you familiar with the term "abstract syntax tree" (AST)? This concept is a bit more involved than type system and is obscure to developers in most programming languages, so I guess you are just didn't meet it yet. In short, AST is the way for a compiler or interpreter to represent your code. In Python, for example, there's a [specialized module](https://docs.python.org/3/library/ast.html) for it, Julia uses `Expr` and `Symbol` types, and in Lisp AST is represented as a nested list of symbols and other objects. – ffriend Oct 17 '20 at 15:02
  • So while your comments are correct for a _function_ `square`, they are totally irrelevant to AST `'(defn square [x] (* x x))`. Because this is not function definition, it is a piece of data, which you can pass to a function, analyze, transform or do whatever you usually do with other data types. Then you can evaluate this AST to actually get function definition. And that function will have a particular type. But until you evaluate this piece of AST, it's just data - list of heterogeneous objects in Clojure. – ffriend Oct 17 '20 at 15:12
  • @ffriend just add the type signature to the "AST" if you want to get technical. Same thing. If you transformation of the AST involves transformation of the same type signature within the AST. Metaprogramming does not eliminate types. – Brian Yeh Oct 18 '20 at 16:07
  • @BrianYeh: I edited post several days ago exactly to address comments about type of AST. Moreover, there's an extended discussion here in comments about the same issue. You continue to ignore all of these. Please, _do read_ them before posting your next comment. – ffriend Oct 18 '20 at 16:42
  • And if you think you read and understood previous comments, please write definition of AST elements as you imagine them with all fields having static types. It's not impossible, so go for it. – ffriend Oct 18 '20 at 16:44
  • @ffriend. An AST is a tree structure. Tree structures are recursive types. `Type Tree a = Leaf a | Node (Tree a Tree a)`. Which says that a Tree is either a Leaf with a generic value `a` or a Node with two 'Trees' The type signature of an AST is just a more complex version of this. In a typed language with meta programming this would be predefined and if you tried to create the wrong structure say Leaf(a, a) the compiler will return a static error before runtime. – Brian Yeh Oct 21 '20 at 03:28
  • @BrianYeh Yes, AST is "just" more complex tree structure. So go on and write down its definition. – ffriend Oct 21 '20 at 13:04
  • @ffriend https://golang.org/pkg/go/ast/ <-- ast defined in golang, a typed language. Go doesn't have a sum type so harder to read. ML has it's AST defined within it's own language as a very readable type: https://www.smlnj.org/doc/Compiler/pages/ast.html. These types are usually predefined with an alias in the programming language. Nobody writes it out, they just rely on pre-compile time type checking. – Brian Yeh Oct 22 '20 at 02:03
  • @BrianYeh Good, so finally we at least have AST type definion and go to the point. As you can see, AST is not "just" more complex, it's much more complex type than programmers usually deal with. And still it's not even closely as powerful as Lisp macrosystem. For example, in Go you can use code generation by an external program (like `go fmt` or `go test`), but you can't transform and run code in the same process. Can you introduce a new control flow statement `unless` (the opposite of `if`)? In Lisp it takes 1 line, in Go it's perhaps not possible at all. – ffriend Oct 22 '20 at 09:48
  • But let's go back to the type of AST. In Lisp in an HTTP framework you can define macro `defroute` to conveniently add routes to a global object of some type `RouteTable`. Obviously, `RouteTable` is not part of AST type definition and yet expression of `defroute` must have a reference to the object of this type. And thus AST type must allow using of `Any` type and avoid compile-time checks. – ffriend Oct 22 '20 at 10:12
  • This is just one simple example to give you an intuition, I have a bunch of much more complex macros in my prijects. It's possible to work around dynamic typing in some of tehm, but the more complex the case the harder it is to keep compile-time checks. I believe, that's why macros aren't popular in statically typed languages. – ffriend Oct 22 '20 at 10:14
  • @ffriend An AST is JUST a tree. That is all it is. I gave an example of a tree binary tree, an AST is just a bigger tree with varying amounts of branches, it is indeed JUST a tree. You can infer the rest of the AST from my initial example. Now for Go you are wrong. You can transform the AST type in a go application, the AST type is used directly for metaprogramming. You aren't clear of what I introduced to you, You can have Go code write other Go code using the AST type and execute it without external assistance. This is the exact same thing for ML that I introduced above. – Brian Yeh Oct 22 '20 at 15:11
  • @ffriend Any typed language can also do your route example via metaprogramming under strict typing. "Any" is supported in most typed programming languages although for your specific example it's actually not needed as the AST has access to to a meta type one rank higher than RouteTable. – Brian Yeh Oct 22 '20 at 15:18
  • @BrianYeh Do you realize that using `Any` type in a statically-typed language is equivalent to dynamic typing? :) Regarding Go, so you say Go has Lisp-like macros? I'm eager to see an example! Please, share an implementation of some simple macro, e.g. `time` or `unless`. – ffriend Oct 22 '20 at 19:36
  • @ffriend We're both clear about `Any` is. What isn't clear is your understanding of types. Let's fix that. Go here to learn more: https://alhassy.github.io/TypedLisp.html . Now that being said if you want to define your own DSL in Go, you would first have to define the entire DSL using types. Specifically, GO has an in built AST type to describe the language of GO, for your own DSL you would define it via Go's type system. – Brian Yeh Oct 23 '20 at 03:58
  • I never said GO has lisp like macros, what I said is that what you want can be achieved with other typed languages, but you must define it fully via types first. In a dynamic language you don't have to do this, but the cost is less safety from runtime crashes. – Brian Yeh Oct 23 '20 at 04:02
  • @BrianYeh So you admit that Go doesn't support advanced still simple metaprogramming like in Lisp, right? You can create a DSL, but it takes much more code and still has limited scope (e.g. you still can't implement `unless` macro), and thus isn't used much in practice. In contrast, Lisp uses macros everywhere. Half of the base language is implemented using macros. And macros are only easy to write if you allow dynamic typing. If you have code snippets to prove th opposite, please share them. Otherwise I don't see the point in arguing about theoretical abilities of static typing. – ffriend Oct 23 '20 at 10:32
  • Regarding TypedLisp article, if you believe I don't understand a particular thing about types, please point specifically to this thing rather than to a generic article. Also it would be great if you didnt do assumptions about what I understand or don't understand. – ffriend Oct 23 '20 at 10:38
  • @ffriend `unless` can be implimented with a function definition you don't even need to use macros. – Brian Yeh Oct 23 '20 at 17:24
  • @BrianYeh Ouch, instead of trying to implement something you decided to personally attack me :) Just to close the loop (and I swear it's my last comment in this thread), `unless` cannot be implemented as a function in a language with eager execution. `unless` takes 2 arguments - condition and a body to be evaluated, and **the body must be evaluated only if condition is `false`**. Functions (again, in a language with eager execution) don't work this way - they first evaluate all its arguments and only then execute their own code. – ffriend Oct 23 '20 at 18:37
  • @ffriend why are you interpreting it as an attack. IS saying you clearly don't understand something an attack? Not in my opinion. It just means you clearly don't understand something. `unless` will take a predicate function as an argument and a body function. Evaluation of body happens according to the predicate. If it evaluates to false than execute the body. In this case you explicitly control evaluation. This happens in even eager evaluated languages as all eagerly evaluated languages will only execute code in the correct logical branch. `x = True, If (x) {evaluted} else {notevaluated}` – Brian Yeh Oct 23 '20 at 19:12
4

Because that's what the world/market needed. No sense in building what's already built.

I hear the JVM already has a statically typed language ;)

Arthur Ulfeldt
  • 90,827
  • 27
  • 201
  • 284