In my opinion, the current tab feature in the REPL works how I want it to. It would be nice to also have these other features too, but maybe with a different key combination, or to move the current feature to a different key at least, to have both features. Also, this exists
I often happily find myself using the .<tab>
completion, but on modules, not variables. This shows all the function names from a module, which usually also contains the interface for whatever object I’m looking at. It works pretty well to figure out what the relevant methods are that I might want to pass my object to.
So in your first example, almost all the functions I care about for the scat
object are defined in Plots
, so I can enumerate those options via Plots.<tab>
.
@which
works pretty well for this.
I think .<tab>
completion would conflict in a problematic way with field, property and module member completion. But I really like the ,<tab>
suggestion, which doesn’t conflict, and also suggests the variable as the first in a list of input arguments.
my own mental load in julia is higher because julia does not warn me when I use non-(type-)declared variables, but auto-initiates many of them. (In fairness, this is often very useful, but sometimes not, and the designers don’t share my preference here [and they are usually right and I am usually wrong]) Also, because Julia has many nooks and crannies (e.g., performance gotcha’s), there is lots to remember. it’s a beast.
but it is a beautiful beast in many ways, still growing up, and I have high hopes for the future. it’s getting better all the time, and the folks designing it are not only very smart, but also quite mature. (this is unusual; for linux, for example, the system is more mature than its head guy, brilliant as he is.)
I don’t quite follow. What does this mean? Do you have any examples?
See also this issue:
https://github.com/JunoLab/Juno.jl/issues/199
Hi Jules,
it is interesting how you are bringing up this subject, since you appear not to be a computer scientist. In essence you are facing the difficulties of working with a non-OO language after years working with Python. Your troubles are well beyond a simple IDE thing, you miss the character of a higher abstraction level language.
Method discovery was one of the aspects that brought OO languages to the front burner in the 1990s. The internet was just starting, Google was a school project and StackExchange did not exist. The ease of browsing through a well document API made coding far easier and greatly boosted code reuse. The Visual prefix to a coding tool became synonym with the browsing functionality you seek.
That said, considering you do not intend to build complex information systems, Julia can still be the language for you. As others suggested before, a tool like Juno can help considerably. Beyond that, with infrastructures like GitLab, ReadTheDocs or StackExchange, coding on functional languages is far from the bleakness of yesteryear. And reading the documentation beforehand can also help you conceptualising your code.
Finally, keep in mind that Julia is still a work in progress. Personally, that is even a bit attractive.
I find my mental load writing programs past a certain complexity level in Julia higher than other languages due to choice paralysis. Which is a good thing, right? Still it’s a problem I struggle with. The type system coupled with macros and multiple dispatch provide a set of very powerful, flexible building blocks.
In something like C++ or python there are constraints imposed on you by the language that make many choices for you. In Julia, I find myself wrecking my head trying to puzzle out the best abstractions/style to design my code around. The awesome payoff is that when I “get it right” I have some very elegant code. Like a shockingly small amount of code that does what would require an order of magnitude more C++ code.
Things that I believe would lessen my mental load:
- formalized support of traits ( holy traits ) or multiple inheritance.
- data inheritance ( maybe? possibly this is a constraint that actually reduces mental load ).
- method discovery ( has been discussed in this thread )
- a stronger functional programming story, I gravitate to FP, and the Julia story is close, but unfinished.
- if not FP then solidifying and supporting an idiomatic Julia style of some sort.
- using, import, include, modules vs scripts story settling down into a solid idiomatic style of some sort.
Can you elaborate on what you mean by data inheritance?
Data inheritance would for example mean that an abstract supertype could have fields which would be inherited by subtypes.
This pattern was realized by
which is not updated for Julia v1.0
I try to leave macros out of everyday programming, and write a composable API that just works. Then, if macros would improve the surface syntax significantly, provide that over the former.
IMO the best way to break out of this is to write something that just works, then iteratively refine as necessary.
I’ve wanted this many times. But I think the way to approach it might be to write really good method and object search facilities first without worrying about the user interface and only then figure out how best to hook that up to tab completion afterwards. For example, being able to search for a method that takes a certain object as it’s first argument and comes from a given module. Or search for what bindings in a given module would be passable to a given method with some of the arguments given. And so on. That alone would already be powerful, interesting to implement and very useful. Given such a search facility, I think that figuring out how to expose it through tab completion could be done more easily. It would also be perfectly reasonable if more advanced method and object search facilities could not be accessed via tab completion and required dropping into the search api—tab completion would just be a convenience for common kinds of searches.
To elaborate a bit, I could imagine something like this:
@call_search ?(_, ?, ..., 12.5, ..., “hi”)
This would return a vector of pairs of objects (or maybe bindings is better since for tab completion you need to complete the code; but going from object to binding could potentially be done separately; otoh, there are far fewer bindings than objects so limiting to bindings in the first place is probably helpful): a function and a second argument such that when out in in place of the ?
s the resulting call would find a method. This idea is totally half baked and very rapidly starts to reinvent datalog, but maybe that’s the right direction? Maybe not.
This is a similar package that works in Julia 1.0: GitHub - rjplevin/Classes.jl: A simple, Julian approach to inheritance of structure and methods
I’d usually write a first draft this way, then throw it away and rewrite a better version. Maybe repeat.
Just starting that first something that works is not as straightforward in Julia as other languages. I think this is because Julia atm lacks a strong opinion on how to write a program. If it was C++ I’d start by writing some classes that mirror the domain entities and reach for some design patterns. No brainer. If it was Clojure I’d think about how I want to transform the data with higher order functions and isolate state changes as best as I can. Python has documented opinions.
In Julia I kind of start with the abstract data types and some functions, but I get to this point where I just start feeling a bit lost. Part of me wants to code like it’s a lisp and another wants to code like it’s C++ and another part makes up a DSL with a bunch of macros.
There are definitely well represented domains which appear to have solid opinions about how to do things. e.g. Machine learning and mathematical modelling. Could be that I’m just not in a well represented domain. I’ll have to work on changing that
Maybe you should try Test-driven development, if you haven’t already. For many people it helps restructuring the code more naturally.
For myself, I think about how I want to represent my data and define custom types if necessary, giving them the general behaviors I want them to have (and if I’m being good writing unit tests for all of those behaviors). Then I start playing with my types and tackling the problem at hand. This usually leads to realizing I’ve done some things wrong and having to change my data representations in some ways. Then I proceed with the problem. Iterate until done. All of that is pretty similar to how I do things in other languages, although I find that designing data types is more limited and therefore simpler in Julia but I may be a bit biased.
FWIW when I did this years advent-of-code I decided to follow the paradigm that I should represent everything as close to the description/my intuitive understanding of the problem as closely as possible. I.e. no looking-through-the-surface-into-the-underlying-maths but just writing the code that represented my thinking. In most cases that worked surprisingly well, though there were a few cases which where clearly intended to be solved by knowing the mathematical algorithm (e.g. the square-summing exercise).
I think of that as the strength of Julia over e.g. R, where you always try to shoehorn the problem into vectorized predefined operations, or C++ where you always start by building up some big type hierarchy (for me, as not a professional programmer always requiring massive mental strain in terms of defining the best hierarchy).
So, with the risk of sounding flippant, maybe the key in Julia is to relax a little bit about the “optimal” approach and just writing the code that feels good to write in terms of your intuition?