6 ms·
I have always been skeptical about 'scientific' languages. Why do you need a special language when any general language + some libraries will do? This is a good
by mastax 10y ago
I have always been skeptical about 'scientific' languages. Why do you need a special language when any general language + some libraries will do? This is a good example of a feature that only really makes sense in a scientific language.
- rrock 10y agoAs a scientist, it's nice to have an option where I can easily espress my problem, and get a speedy solution at the end. Julia hits this sweet spot. I translated some of my code from one of those general purpose languages to Julia 0.2, years ago. It was both more readable, and 20x faster. Never looked back.
- nerdponx 10y agoIn R, I can import a CSV, plot a histogram of each column, and fit a linear regression of one column against the others in about 5 minutes and 15 lines of code. In Python, I can do the same thing if I install the Pandas and Statsmodels libraries first. Try that in Ruby, Perl, C++, C, Java, Rust, Haskell, Common Lisp, or just about any language you can think of. Good luck.
- goatlover 10y agoThe other great thing about R is the amount of information it outputs when you run a stats function.
- nerdponx 10y agoIt's easy to forget that R inhabits a gray area between a full-fledged programming language and a "statistics package" like SAS or SPSS or Stata or GRETL.
- deleted 10y ago[deleted]
- StefanKarpinski 10y agoNumerical programming takes more expressive power than one might imagine. In most languages, numerical primitives like integers and floating-point numbers, and numerical operators like `+` and `[]` (array indexing), are very special and are endowed with enough magic to be usable. E.g. in C, `+` is too polymorphic to be defined as a function; in Python, `+` has special `__radd__` methods to (hackily) emulate multiple dispatch; in Java `int`, `float` and `double` are entirely different kinds of values (non-objects) from normal user-definable objects. In many ways, the fundamental premise of Julia is to design a language with sufficient power and performance that numbers are not special: "primitive" types like `Int`, `Float64` are just defined in normal Julia code, and operators like `+` and `[]` are normal Julia functions like any other. This recent post gives some more motivation: https://discourse.julialang.org/t/julia-motivation-why-werent-numpy-scipy-numba-good-enough/2236/10 https://discourse.julialang.org/t/julia-motivation-why-weren....
- zengid 10y agoI like this quote from the 'motivation' link above: "A lot of Julia's design fell directly out of making it easy to generate LLVM IR – in some ways, Julia is just a really great DSL for LLVM's JIT." Excited to learn more
- jamii 10y agoJulia is my language of choice for writing compilers. Quasiquoting means that codegen is as easy as string interpolation is in other languages. quote let $(index_inits...) $(results_inits...) if $(reduce((a,b) -> :($a && $b), true, index_checks)) let $(var_inits...) # declare vars local in here so they can't shadow relation names $body end end tuple($(results...)) end end Great introspection into the inference and compilation pipeline, directly from the repl. julia> function double(xs) [2*x for x in xs] end double (generic function with 1 method) julia> double([1,2,3]) 3-element Array{Int64,1}: 2 4 6 julia> @code_warntype double([1,2,3]) Variables: xs::Array{Int64,1} #s1::Int64 #s2::Int64 #s3::Int64 x::Int64 #s4::Int64 Body: begin # none, line 2: GenSym(1) = (Base.arraylen)(xs::Array{Int64,1})::Int64 0: GenSym(3) = (top(ccall))(:jl_alloc_array_1d,(top(apply_type))(Base.Array,Int64,1)::Type{Array{Int64,1}},(top(svec))(Base.Any,Base.Int)::SimpleVector,Array{Int64,1},0,GenSym(1),0)::Array{Int64,1} #s1 = 1 #s2 = 1 #s3 = 0 unless (Base.box)(Base.Bool,(Base.not_int)(#s3::Int64 === GenSym(1)::Bool)) goto 2 3: #s3 = (Base.box)(Base.Int,(Base.add_int)(#s3::Int64,1)) GenSym(9) = (Base.arrayref)(xs::Array{Int64,1},#s2::Int64)::Int64 GenSym(10) = (Base.box)(Base.Int,(Base.add_int)(#s2::Int64,1)) #s4 = 1 GenSym(11) = GenSym(9) GenSym(12) = (Base.box)(Base.Int,(Base.add_int)(1,1)) x = GenSym(11) #s4 = GenSym(12) GenSym(13) = GenSym(10) GenSym(14) = (Base.box)(Base.Int,(Base.add_int)(2,1)) #s2 = GenSym(13) #s4 = GenSym(14) GenSym(4) = (Base.box)(Int64,(Base.mul_int)(2,x::Int64)) $(Expr(:type_goto, 0, GenSym(4))) $(Expr(:boundscheck, false)) (Base.arrayset)(GenSym(3),GenSym(4),#s1::Int64)::Array{Int64,1} $(Expr(:boundscheck, :(Main.pop))) #s1 = (Base.box)(Base.Int,(Base.add_int)(#s1::Int64,1)) 4: unless (Base.box)(Base.Bool,(Base.not_int)((Base.box)(Base.Bool,(Base.not_int)(#s3::Int64 === GenSym(1)::Bool)))) goto 3 2: 1: return GenSym(3) end::Array{Int64,1} julia> @code_llvm double([1,2,3]) define %jl_value_t* @julia_double_21481(%jl_value_t*, %jl_value_t**, i32) { top: %3 = alloca [4 x %jl_value_t*], align 8 %.sub = getelementptr inbounds [4 x %jl_value_t*], [4 x %jl_value_t*]* %3, i64 0, i64 0 %4 = getelementptr [4 x %jl_value_t*], [4 x %jl_value_t*]* %3, i64 0, i64 2 %5 = getelementptr [4 x %jl_value_t*], [4 x %jl_value_t*]* %3, i64 0, i64 3 %6 = bitcast [4 x %jl_value_t*]* %3 to i64* store i64 4, i64* %6, align 8 %7 = getelementptr [4 x %jl_value_t*], [4 x %jl_value_t*]* %3, i64 0, i64 1 %8 = load i64, i64* bitcast (%jl_value_t*** @jl_pgcstack to i64*), align 8 %9 = bitcast %jl_value_t** %7 to i64* store i64 %8, i64* %9, align 8 store %jl_value_t** %.sub, %jl_value_t*** @jl_pgcstack, align 8 store %jl_value_t* null, %jl_value_t** %4, align 8 store %jl_value_t* null, %jl_value_t** %5, align 8 %10 = load %jl_value_t*, %jl_value_t** %1, align 8 %11 = getelementptr inbounds %jl_value_t, %jl_value_t* %10, i64 1 %12 = bitcast %jl_value_t* %11 to i64* %13 = load i64, i64* %12, align 8 store %jl_value_t* inttoptr (i64 140140655870352 to %jl_value_t*), %jl_value_t** %5, align 8 %14 = call %jl_value_t* inttoptr (i64 140149385306688 to %jl_value_t* (%jl_value_t*, i64)*)(%jl_value_t* inttoptr (i64 140140655870352 to %jl_value_t*), i64 inreg %13) store %jl_value_t* %14, %jl_value_t** %4, align 8 %15 = icmp eq i64 %13, 0 br i1 %15, label %L4, label %L1.preheader L1.preheader: ; preds = %top %16 = load i64, i64* %12, align 8 %17 = bitcast %jl_value_t* %10 to i64** %18 = bitcast %jl_value_t* %14 to i64** br label %L1 L1: ; preds = %idxend, %L1.preheader %"#s3.0" = phi i64 [ %22, %idxend ], [ 0, %L1.preheader ] %"#s2.0" = phi i64 [ %26, %idxend ], [ 1, %L1.preheader ] %19 = add i64 %"#s2.0", -1 %20 = icmp ult i64 %19, %16 br i1 %20, label %idxend, label %oob oob: ; preds = %L1 %21 = alloca i64, align 8 store i64 %"#s2.0", i64* %21, align 8 call void @jl_bounds_error_ints(%jl_value_t* %10, i64* nonnull %21, i64 1) unreachable idxend: ; preds = %L1 %22 = add i64 %"#s3.0", 1 %23 = load i64*, i64** %17, align 8 %24 = getelementptr i64, i64* %23, i64 %19 %25 = load i64, i64* %24, align 8 %26 = add i64 %"#s2.0", 1 %27 = shl i64 %25, 1 %28 = load i64*, i64** %18, align 8 %29 = getelementptr i64, i64* %28, i64 %19 store i64 %27, i64* %29, align 8 %30 = icmp eq i64 %22, %13 br i1 %30, label %L4.loopexit, label %L1 L4.loopexit: ; preds = %idxend br label %L4 L4: ; preds = %L4.loopexit, %top %31 = load i64, i64* %9, align 8 store i64 %31, i64* bitcast (%jl_value_t*** @jl_pgcstack to i64*), align 8 ret %jl_value_t* %14 } julia> @code_native double([1,2,3]) L128:L159:L177: .text pushq %rbp movq %rsp, %rbp pushq %r15 pushq %r14 pushq %rbx subq $40, %rsp movabsq $jl_alloc_array_1d, %rax movabsq $140140655870352, %rdi # imm = 0x7F750A030190 movq $4, -56(%rbp) movabsq $jl_pgcstack, %r15 movq (%r15), %rcx movq %rcx, -48(%rbp) leaq -56(%rbp), %rcx movq %rcx, (%r15) movq $0, -40(%rbp) movq $0, -32(%rbp) movq (%rsi), %r14 movq 8(%r14), %rbx movq %rdi, -32(%rbp) movq %rbx, %rsi callq *%rax movq %rax, -40(%rbp) cmpq $0, %rbx je L159 xorl %ecx, %ecx movq 8(%r14), %rdx nopw %cs:(%rax,%rax) cmpq %rdx, %rcx jae L177 movq (%r14), %rsi movq (%rsi,%rcx,8), %rsi shlq $1, %rsi movq (%rax), %rdi movq %rsi, (%rdi,%rcx,8) incq %rcx cmpq %rcx, %rbx jne L128 movq -48(%rbp), %rcx movq %rcx, (%r15) leaq -24(%rbp), %rsp popq %rbx popq %r14 popq %r15 popq %rbp retq movq %rsp, %rsi addq $-16, %rsi movq %rsi, %rsp addq $1, %rcx movq %rcx, (%rsi) movabsq $jl_bounds_error_ints, %rax movl $1, %edx movq %r14, %rdi callq *%rax It catches type errors early, thanks to the typed multiple dispatch. julia> xs = [] 0-element Array{Any,1} julia> push!(xs, 42) 1-element Array{Any,1}: 42 julia> push!(xs, "foo") 2-element Array{Any,1}: 42 "foo" julia> ys = Int64[] 0-element Array{Int64,1} julia> push!(ys, 42) 1-element Array{Int64,1}: 42 julia> push!(ys, "foo") ERROR: MethodError: `convert` has no method matching convert(::Type{Int64}, ::ASCIIString) This may have arisen from a call to the constructor Int64(...), since type constructors fall back to convert methods. Closest candidates are: call{T}(::Type{T}, ::Any) convert(::Type{Int64}, ::Int8) convert(::Type{Int64}, ::UInt8) ... in push! at ./array.jl:432 Plus, I only have to think in one language, but I can write sloppy dynamic heap-allocating-everywhere code in the compiler and with just a bit of thinking emit zero-allocation statically-dispatched code in the output.
- deleted 10y ago[deleted]
- jowiar 10y agoOne of the big wins is being REPL/workbook-focused, rather than REPL/workbook as an afterthought. When I'm building software, I tend to like things to have very tight interfaces -- box everything up into components, understand how they talk to each other, etc. Define interfaces, implementations, types, etc -- In general, optimizing for long-term maintainability. When I'm exploring data, my thought process is much more "scatter everything about on the desk" and "let me run these 5 lines of code again within the current context". "What does this thing look like", etc. Having "a table of data" as a first-class citizen in the language, with all the libraries assuming that as input and everything optimized to work around / display / visualize such is incredibly useful.
- dnautics 10y agoI would say there's something magical about the way that Julia is structured that makes it more than just a scientific language. I wrote a DSL for Julia for writing clean combinatorial verilog (haven't tackled sequential yet) in 3 days, using lispish macros. It took another 1 day to hook it up to 'verilator' which transpiles the verilog to C and lets you crosscheck it by loading it back up in Julia. https://github.com/interplanetary-robot/Verilog.jl https://github.com/interplanetary-robot/Verilog.jl This is very important if you're planning to build hardware to do specific math - because Julia is incredibly good at mathematical modeling, and you can very rapidly set up comprehensive tests for your hardware designs with confidence. The closest alternative is chisel, which is written in scala. Although it's more professionally maintained and more fully-featured, it's hard to call the verilog chisel emits "human-readable", and it's harder to set up comprehensive tests - berkeley hardfloat, which is a very impressive project in chisel, had several critical bugs in its implementation (that I found, using julia).
- UncleOxidant 10y agoThat's really nice. Now we need VHDL.jl as well - maybe different HDL backends could be bolted on? Julia is much better suited for this than Scala due to Julia's macro system.
- dnautics 10y agoAlthough I don't know that much about VHDL, it probably won't be that hard to either fork it or make it an option. Drop a line. My contact info is in my profile.
- digitalzombie 10y agoBecause in R NA is built into a language. So it understand the concept of missing value and works with logical operation. Also it have factor concept into the language too. General Language may not have the tool to deal with missing value and having a library doesn't necessary means it's good as a built-in concept NA. Python panda and such does not handle NA as beautifully as R. (http://pandas.pydata.org/pandas-docs/stable/missing_data.html http://pandas.pydata.org/pandas-docs/stable/missing_data.htm...) Python Panda default to Null and before that it uses something else to represent missing data. Null value in general is the last thing you do when you don't know how to represent a certain type. If you have a type language with sum type and pattern matching then you don't even need null. And yes R have its own Null type so NA is separate from it. Erlang have PID as a primitive. I think any domain specific languages are smaller in rules and syntax, and it makes it very very easy to learn for experts and people of those domains.
- goatlover 10y agoLanguages like Julia, R and Matlab offer a whole lot in the way of built-in syntax, data structures, functions and constants for scientific computing that you would have to get from libraries in general purpose languages. But unless those languages have the kind of libraries that Python offers, such as Numpy and Pandas, you're not going to have that kind of support. In addition, scientific languages will have libraries mostly in that domain. You can't beat R's library when it comes to statistics. They will also have good plotting libraries. What you also get with Julia and Fortran is code designed to be optimized for numerical computing. Python attempts to offer this kind of performance via Numpy, which is Python wrapper on C or Fortran BLAS library. Or by JIT compiling with Numba, which is something Julia does automatically the first time you call a function.
- coldtea 10y ago>Why do you need a special language when any general language + some libraries will do? Because you need special math support, easy access to suitable operators (and/or overloading), etc.
- jtraffic 10y agoPeople don't "need" special languages. They just like them.
- tnecniv 10y agoI've done mathematical programming in both C++ and MATLAB. As much as I hate MATLAB as a language, it's way faster and easier to prototype things. There's a huge library of vetted functions for scientific things I'm interested in, and having matrices built into the language is great. MATLAB has tons of warts though, so I'm looking forward to switching to Julia this summer.
- dnautics 10y agoOne of the biggest warts is the casual elision of vectors and nx1 matrices... (and scalars and 1-arrays and 1x1-matrices). Julia wrestled with this in its early days, but I think the way it does things now is quite nice.
- tnecniv 10y agoLet's not forget the complete lack of package management, where it guesses at which of the many overlapping functions you intended to call.
- e12e 10y agoI'm not sure if "special language" is a particularly apt description of what Julia is. SQL is a "special" language. At any rate - a lisp-like language with llvm backend, package manager, and a (small, but not trivial) group of users writing real software - what's not to like? I (still) recommend the talk: "Julia - to lisp or not to lisp?", that gives a quick overview of some of the design choices: https://youtu.be/dK3zRXhrFZY https://youtu.be/dK3zRXhrFZY For me, I like that it has a real story trying to balance "actual integer math" (not this silly machine constrained twos-compliment hack" and "you could conceivably write a ray tracer that wasn't unusably slow" :)