Programming thread

The syntax objection I have with Haskell is that it's got significant whitespace.

The main syntax problem I have with Haskell is its weird declarations at the top of functions. For example, the top of a custom quicksort function might read as:
Code:
quicksort :: (Ord a) => [a] -> [a]
I think that it's not strictly necessary, but it still fucks with my head. Why can't it have sane headers like Java or C?

I hate Python for that and I hate Haskell for it too.

I know exactly what you mean. I've chosen to get around the problem by going full pleb and using a python-specific IDE whenever I need to use python.
 
The main syntax problem I have with Haskell is its weird declarations at the top of functions. For example, the top of a custom quicksort function might read as:
Code:
quicksort :: (Ord a) => [a] -> [a]
I think that it's not strictly necessary, but it still fucks with my head. Why can't it have sane headers like Java or C?
In Ocaml, you might see a function like:
Code:
let format_name first_name last_name =
  first_name ^ " " ^ second_name
It's type declaration would be:
Code:
val format_name : string -> string -> string
Now, that's basically a function called format_name that takes two arguments, first_name and last_name, and returns a string.

Example usage in the repl:
Code:
utop # format_name "Marvin" "GotShotInTheFace";;
- : string = "Marvin GotShotInTheFace"
The problem with this is that it's not really the full story.

Something that trips up newcomers to certain functional languages, is that technically speaking, a function has strictly one input and one output.

So "string -> string -> string" isn't a function that takes two arguments. It's really a function that takes a string, and it returns "string -> string". Which is a function that takes a string, which returns a string.

In Javascript, it's the equivalent to:
Code:
function format_name(first_name) {
  return function(last_name) {
    return first_name + " " + last_name;
  };
}
And you'd call it like:
Code:
> format_name("Marvin")("GotShotInTheFace");
'Marvin GotShotInTheFace'

Most of the time, you're writing the same code (eg creating a function with two args that returns a string) as the non-functional language, but there are slightly different semantics under the hood.

These different semantics get useful when you deal with functions that take other functions as input. For example, there's the map function that is in a lot of languages' standard libraries.

Map basically takes a list (or array or whatever), and a function (or in C++ or Java, an object with a Call() method), and then passes the function over the items in the list, and returns the results of that in a new list.
Code:
> map(function(name) { return name + " is fat and I wouldn't have sex with them"; },
      ["Kengle", "cwc", "pixyteri"])
[ 'Kengle is fat and I wouldn\'t have sex with them',
  'cwc is fat and I wouldn\'t have sex with them',
  'pixyteri is fat and I wouldn\'t have sex with them' ]

Because functions in Haskell, Ocaml and others are designed this way, you have the option to only fill in some of the arguments, and apply them later, like in a map function.

Made up example, in a vaguely Ocaml-ish syntax:
Code:
(* a function to fetch a user from a database handle: *)
let get_lolcow_by_name db_handle name =
  ... (* omitted *)

(* main script code: *)
let lolcow_names = ["Kengle"; "cwc"; "pixyteri"]

let my_database_handle = open_db()

(* we can fetch our lolcows a few ways...

    we can override the db_handle and assign the
    resulting function to a var: *)
let db_fetch_lolcow = get_lolcow_by_name my_database_handle

let lolcows = List.map db_fetch_lolcow lolcow_names

(* or we don't even need to assign it to a variable,
   we can use parentheses and fill in the db_handle on the fly: *)
let lolcows =
  List.map
    (get_lolcow_by_name my_database_handle)
    lolcow_names

For the record, here are the types of the functions/variables in the above snippet:
Code:
val get_lolcow_by_name : db_handle -> string -> lolcow_obj

val lolcow_names : string list

val my_database_handle : db_handle

val db_fetch_lolcow : string -> lolcow_obj
  (* see above, we filled in the first arg, the db_handle, and all
      that's left is the string -> lolcow_obj part *)

val lolcows : lolcow_obj list
A more traditional "function(typeA, typeB) returnType" arrangement kind of obscures this detail. You'd have programmers fill in the first arg, and then the type of that new function would be "function(typeB) returnType".


It's more accessible to newcomers, but I think it's confusing in the long run.
 
@Marvin, this doesn't follow directly from your post, but I find it interesting that you use Ocaml as your example language. I only know one person IRL who uses it, and he needed to learn it recently for an internship with some investment company.
Heh, Jane Street, per chance? They've got fantastic technology for an investment company.

There's a few other companies that use Ocaml. They basically use it for really smart stuff that needs to have great performance.

Ocaml, SML and if we want to get extra esoteric, Mythryl, are in the same family of languages. Strict typing, ordinary evaluation order. Ocaml's the most popular and it's got a bunch of tools and utilities and languages.

Haskell's related to those three. It's got a similar type system (with a few extensions) but a different evaluation order.

Because of the strict typing, there's a huge potential for optimization with these languages. Like serious optimization.

Few people use SML, but one point in its favor is MLton, which is just an elaborate experiment in compiler optimization.

Compare C++ and MLton with some pretty complicated number crunching.

MLton is beating C++ at raytracing. And MLton (and the rest of these languages) is a GC'd language, no manual memory management.

The MLton code is slightly shorter. I'd argue it's clearer than the C++ code, but that's a matter of personal taste. Also very few explicit type annotations are required, compared to the C++.

Of course, they're not the solution to everything. They're terrible at writing code that needs to change a lot. The strict typing is like BDSM typing, whips and chains, bondage and discipline.

So I divide my projects into two categories: rigid and fast (servers, video processing, etc) and dynamic and flexible (gamedev).

For me, Ocaml for the former. Scheme for the latter.
 
Something that trips up newcomers to certain functional languages, is that technically speaking, a function has strictly one input and one output.

So "string -> string -> string" isn't a function that takes two arguments. It's really a function that takes a string, and it returns "string -> string". Which is a function that takes a string, which returns a string.
Forgot to comment on this. You explained that infinitely better than the /g/ recommended learning resources for Haskell. I was attracted to learning Haskell because of its "lazy" evaluation and how easy it makes creating and manipulating sets, but those function headers, man...
*sigh*

Heh, Jane Street, per chance? They've got fantastic technology for an investment company.
Yep, right on the money. Is that something you just knew or did you look it up?

if we want to get extra esoteric, Mythryl
Wow, you're not kidding. I thought my distro had a package for every language. I was wrong.

Scheme for...dynamic and flexible
Can you speak to speed and resource use of Scheme compared to Python? Just because Python fills the niche of dynamic and flexible in the public consciousness.
EDIT: Actually, maybe just a comparison in speed and resource use of your favorite variety of Scheme vs Python, since the former is so huge.
 
Last edited:
then again, with complicated system setups, garbage collection can mean some horrible bugs, like when you are using different standard librairies/virtual machines at once (quite common with real applications)
 
then again, with complicated system setups, garbage collection can mean some horrible bugs, like when you are using different standard librairies/virtual machines at once (quite common with real applications)

That's definitely true; I was messing around with a C# Vulkan wrapper and I kept getting inexplicable crashes after like ~30 seconds of uptime or when ever the rendering window was moved slightly off screen. Didn't make any sense, just an access violation in external code at random intervals.

Well, I fucked around a bunch and eventually figured out that the wrapper wasn't storing the all the debugger callbacks on the C# side, so their handles were getting collected randomly. The wrapper author is currently working on getting that fixed, but he has to rewrite the code generator to do so lol.

Still, I vastly prefer to only have to think about memory management sometimes vs all of the time. It makes most things so much simpler.
 
Anyone doing anything with Golang?
Yep, right on the money. Is that something you just knew or did you look it up?
They're a well known user of Ocaml.

Ocaml doesn't have a great standard library (the standard library is basically meant to bootstrap the compiler itself), but Jane Street's Core library is many people's default library.
Wow, you're not kidding. I thought my distro had a package for every language. I was wrong.
The story behind Mythryl's kinda sad because this brilliant guy had been programming for years, and he's seen basically everything. So he decides to make his own language, using the best parts of everything he's worked on.

Basically aiming for a modern SML dialect, but one that's heavily influenced by Unix sensibilities. He's basically making the system's language that C should've been. He got started and hammered away it and made an excellent language. I mean, it would still take a lot of work (and a lot of volunteers) to turn it into production quality, but he made great strides in laying down the foundation.

And then... he got cancer and died a few years after he started.

I think Ocaml is basically what he wanted (although Ocaml's less Unix-ey), but yeah.

(Heh, though his choice of if/fi blocks, a la shell languages makes me cringe.)
Can you speak to speed and resource use of Scheme compared to Python? Just because Python fills the niche of dynamic and flexible in the public consciousness.
Depends on the implementation. Schemes can be very fast with a good compiler.

Python's got a single implementation and language spec (with a few, somewhat obscure alternatives), and it's bytecode based. Good for portability, not great for speed, but most Python problems that need speed just offload to C modules. It gets the job done and it's readable for a big chunk of people out there.

A big reason for Scheme over Python is that Scheme's nicer to work in a repl with. Whitespace significant languages don't take well to typing shit out on the fly, outside of an editor.
then again, with complicated system setups, garbage collection can mean some horrible bugs, like when you are using different standard librairies/virtual machines at once (quite common with real applications)
Like bugs involving things getting GC'd inappropriately? Or just GC pauses coming at the wrong times?

I mean, ultimately a garbage collector is just another tool. You should have a decent idea about how it works (moving vs non-moving, generational, etc). You shouldn't have to tune your GC much, but if you're doing something that's both: complicated (working with trees) and requires high performance, you might need to bust out the manual and figure out how to mess with your GC settings. Like things like heap size.

It's not going to solve every problem. And you still need to know how to manage resources. Managing file handles with GC is retarded (in many cases), so you should still hustle and practice doing that. But it's a useful tool to have in your arsenal. GC will make many, many programs you write better.

One of my favorite programming books has a whole chapter about the innards of the runtime system. You shouldn't need to fuck with it for most programs, but when you do need to, it's a lifesaver.
 
I think the most common error would be an object getting freed when it's in use by an external library. Some language allow to explicitly prevent an object and what he references to be freed and sometimes you have to use work-around that leads to heisenbugs.

The worst case is an object spawned by one (standard) library and collected by the garbage collector of another.

I meant complicated on a system level, not algorithmic: multiple librairies compiled by different teams, using different compilers, different processes exchanging custom types, etc...
 
I think the most common error would be an object getting freed when it's in use by an external library. Some language allow to explicitly prevent an object and what he references to be freed and sometimes you have to use work-around that leads to heisenbugs.

The worst case is an object spawned by one (standard) library and collected by the garbage collector of another.

I meant complicated on a system level, not algorithmic: multiple librairies compiled by different teams, using different compilers, different processes exchanging custom types, etc...
GC is usually on a language level. Thus all libraries would use the same GC. (Or no GC for the C libraries.)

But yeah, resource management in general is a a really important skill to know whether you're using GC or not. Like file handles. You can't leave those to the garbage collector for anything network facing.

If attackers can guess that an operation opens up a lot of file handles, they can hammer that endpoint or whatever and exhaust all your file handles. And the next attempt to open a file will fail and probably crash the process.

It's why database frameworks usually maintain a pool of connections in lieu of opening a fresh connection for each request.

Heh, for my *nix friends out there: what's "ulimit -n" tell you? I get 1024.

That's the number of file handles a single process can have open. (It's configurable though, but it's good to write code that responsibly closes file handles regardless.)
 
I'm embarrassed to be posting here because I'm a total amateur but I'm doing some programming stuff (It's all Python, which should tell you all you need to know) and I want to know how to comment code effectively
At the moment I have 100+ line functions and classes which have absolutely no commenting and I need to add some but I'm too lazy and don't quite know what to write
 
I'm embarrassed to be posting here because I'm a total amateur but I'm doing some programming stuff (It's all Python, which should tell you all you need to know) and I want to know how to comment code effectively
At the moment I have 100+ line functions and classes which have absolutely no commenting and I need to add some but I'm too lazy and don't quite know what to write
Firstly you should try to "make your code the comments", meaning it's written in a way that makes it's intent clear, which is obvious and vague advice. If you're doing something for a non-obvious reason, put a comment in there. eg. non-obvious if comparison, non-obvious math. I also like to use block quotes to break up sections inside a single scope.

if (atlas.count(code) == 0) // if char not found generate glyph

else { // if not enough horizontal space

[...]
GPU_SetBlending(oldFontPage, false); // two blends otherwise (blurry as fuck)
[...]
 
Firstly you should try to "make your code the comments", meaning it's written in a way that makes it's intent clear, which is obvious and vague advice. If you're doing something for a non-obvious reason, put a comment in there. eg. non-obvious if comparison, non-obvious math.
This.

You should use the structure of the code itself and your naming conventions to describe what it's doing.

Reason being, then the meaning of your code is baked into its ability to function. Problem with comments is that they require maintenance. No one maintains comments.
 
I would add a comment to the beginning of each function/class describing what it does, or what it is for, and then add comments to the internals of the function/class with a general description of what each section of code does. You don't need to comment every line or statement of course, just give an idea of what a section of code does.

Presumably the maintainer will know how the language works, so unless you are doing some black magic nonsense, you don't necessarily need to specify how the code does what it does. For example, comments like
Code:
        int a = b + 10;   // Add ten to b
are completely pointless. If the reader knows that little, they won't be able to read your code anyway.

This code is in C# so the symbol '//' starts a comment, and '///' starts an xml documentation block (which is pretty much just a fancy comment).

'public int IndexOf(T item)' declares a function named 'IndexOf' that returns the index of an object named 'item' in an array. It's part of a class that represents sparse arrays.
Code:
        /// <summary>
        /// Get the index of <paramref name="item"/>
        /// </summary>
        /// <param name="item">The item to search for</param>
        /// <returns>the index of the item, or -1 if the item was not found</returns>
        /// <remarks>this will also search for gaps if <paramref name="item"/> is equal to <see cref="defaultValue"/></remarks>
        public int IndexOf(T item)
        {

            int i = -1;
            if (tailIndex_ > 0) // skip if no values are in the array
            {
                foreach (var pair in ValidValues)
                {
                    if (valueComparer_.Equals(pair.value, item)) // check all valid values for item
                    {
                        i = pair.i;
                        break;
                    }
                }
            }

            if (valueComparer_.Equals(defaultValue_, item)) // if item is the default value
            {
                if (tailIndex_ > 0) // skip if there are no items in the array
                {
                    var prev = inner_[0].i;
                    if (i > -1) // if there is a result from checking valid values pick the minimum
                    {
                        int dI = -1;
                        foreach (var pair in ValidValues.Skip(1)) // check for gaps between valid values
                        {
                            if (pair.i > i) // don't bother looking past the best result for the previous search
                                break;
                            if (pair.i - prev > 1)
                            {
                                dI = prev;
                                break;
                            }
                            prev = pair.i;
                        }
                        if (dI > -1 && dI < i)
                            i = dI;
                    }
                    else
                    {
                        foreach (var pair in ValidValues.Skip(1)) // check for gaps between valid values
                        {
                            if (pair.i - prev > 1)
                            {
                                i = prev; // return the first index in the gap
                                break;
                            }
                            prev = pair.i;
                        }
                    }
                }
                else if (count > 0) // if there are no items, and the array is not empty, return the first index
                    i = 0;
            }
            return i;
        }
I tried to color the code to make it more readable, but the code block erases coloring *sigh*

As you can see, I'm not describing how each piece works, I'm just describing what each piece does.
 
Last edited:
Not programming, but I just wanted to say that I fucking hate The Register and their stupid headlines and stupid writers. They're not witty, they're not funny, they're not good at writing. Bleeping Computer is a way better IT news site.
 
I do not understand why programs like Asterisk hang on to this bullshit concept of limiting things to 80 characters for no reason.

Case in point: for the last two weeks I've been fighting Asterisk on an 80 character limit for channel names. In order to dial out to this provider I need to provider several uri parameters, and the old hacky way we did it was to just dial out using the REST API and name the channel the entire dial string + uri parameters, but the proper way to do it in Asterisk is to rewrite the entire dialplan for the customer facing side of the company and make sure it integrates into three other SIP providers as well as the one provider that's broken.

I hate legacy BS.
 
Controversial opinion: It is no longer okay to dislike Java because it is slow, since it has gotten faster. However, it is completely okay to dislike Java because of the resource overhead associated with the JVM and the relatively long program start-up times when the JVM isn't already running.
I do not understand why programs like Asterisk hang on to this bullshit concept of limiting things to 80 characters for no reason.
I have come to the conclusion that software is not designed to satisfy all consumer expectations for the product. It is designed to satisfy minimum consumer expectations, and further amenities are discouraged because they raise consumer expectations.
 
Controversial opinion: It is no longer okay to dislike Java because it is slow, since it has gotten faster. However, it is completely okay to dislike Java because of the resource overhead associated with the JVM and the relatively long program start-up times when the JVM isn't already running.

I have come to the conclusion that software is not designed to satisfy all consumer expectations for the product. It is designed to satisfy minimum consumer expectations, and further amenities are discouraged because they raise consumer expectations.

you do realise ressource overhead and start up time is part of what makes a program slow, right?
Fast is not just fast on the highest cpu with infinite memory and not counting time to start and exit.
 
Back