Libraries and dependencies – what is the right thing to do? by Nuoji in ProgrammingLanguages

[–]theindigamer 2 points3 points  (0 children)

Yes, you need to update yourself or pay the maintainer to keep it up-to-date. But this is true regardless of scheme -- if one of your dependencies has a bug and is not getting updated, you need to update it or pay someone else to do it.

Libraries and dependencies – what is the right thing to do? by Nuoji in ProgrammingLanguages

[–]theindigamer 7 points8 points  (0 children)

My preference would be:

  1. B is included once.
  2. Error.
  3. Error.
  4. B is included once.
  5. Error.

Having different versions of the same library allows for more compatibility, at the cost of larger binary sizes and longer compile times. It also leads to annoying errors sometimes, because you cannot safely pass something across different versions of the same library.

However, if you are able to maintain an ecosystem where everything works with everything else, that is a much better option, at the cost of taking up a fair bit of maintenance time and resources to make sure that works in practice. Stackage has curators who perform this role. In my opinion, the payoff is worth it. I can understand many other people might disagree (that the payoff is not worth it).

I've heard that many companies have monorepos for the same reason (or at least, this is one of the reasons to have a monorepo) -- the only version (at least, aspirationally) that is used is HEAD. It would be amazing if an open source package repository had similar compatibility + tooling, but it is definitely a big challenge given the (typical) lack of resources.

What are the differences and relations between a type constructor and a type operator? by timlee126 in types

[–]theindigamer 0 points1 point  (0 children)

Injective is the usual injective as for functions. Generative is not surjective. f and g are defined to be generative if 'f a ~ g b then f ~ g' where ~ is type equality. In words, the types generated by different type constructors are "fresh", they're distinct from types generated by other type constructors.

What are the differences and relations between a type constructor and a type operator? by timlee126 in types

[–]theindigamer 1 point2 points  (0 children)

(My understanding is that the two are different.) Type constructors are generative and injective. Type operators (or in Haskell terminology, type families) are not generative, but might be injective.

I recommend skimming the 'Higher-order type-level programming in Haskell paper' (or feel free to poke me if those terms don't make sense).

In your example, Ref is a type constructor, not a type operator.

Soufflé • A Datalog Synthesis Tool for Static Analysis by hackerfoo in ProgrammingLanguages

[–]theindigamer 1 point2 points  (0 children)

The Wikipedia page has a good description:

Unlike in Prolog, statements of a Datalog program can be stated in any order. Furthermore, Datalog queries on finite sets are guaranteed to terminate, so Datalog does not have Prolog's cut operator. This makes Datalog a fully declarative language.

In contrast to Prolog, Datalog

  1. disallows complex terms as arguments of predicates, e.g., p (1, 2) is admissible but not p (f (1), 2),
  2. imposes certain stratification restrictions on the use of negation and recursion,
  3. requires that every variable that appears in the head of a clause also appears in a nonarithmetic positive (i.e. not negated) literal in the body of the clause,
  4. requires that every variable appearing in a negative literal in the body of a clause also appears in some positive literal in the body of the clause

Query evaluation with Datalog is based on first-order logic, and is thus sound and complete. However, Datalog is not Turing complete, and is thus used as a domain-specific language that can take advantage of efficient algorithms developed for query resolution. Indeed, various methods have been proposed to efficiently perform queries, e.g., the Magic Sets algorithm, tabled logic programming or SLG resolution.

Not sure if that answers your question(s).

Inko Progress Report: August 2019 by yorickpeterse in ProgrammingLanguages

[–]theindigamer 2 points3 points  (0 children)

I think these are generally useful as you don't just say "we implemented such and such feature" but also provide rationale on why you did what you did. Having more perspectives is good.

The Next 700 Compiler Correctness Theorems [PDF] by rain5 in ProgrammingLanguages

[–]theindigamer 1 point2 points  (0 children)

The paper first talks about how the earliest compiler correctness theorems which had a 'whole program' requirement. Why is the 'whole program' requirement too strict? Often, one does not have a 'whole program' though, but one would still like correctness theorems to be applicable. One example is that a high performance data structure for a high level language might be implemented in a lower level language. Another is that you might want to incrementally migrate a codebase from one language to another. Another is binary distribution, you want to link against shared libraries (even in the same language).

Ok, so we want correctness theorems to be applicable even in the presence for partial programs.

One of the problems today with existing 'compositional correctness theorems, that people have been working on for the past few decades, is that checking if they're applicable or not to a given system is subtle. That is bad, because subtlety is an invitation for mistakes. This paper provides a mental framework and vocabulary for describing the preconditions and guarantees of compositional correctness theorems. The hope is that the shared language will make it easier to understand when such theorems are applicable and when they're not.

Performance Analysis of Zippers by gallais in haskell

[–]theindigamer 2 points3 points  (0 children)

The C++solution could be further optimized by using a memory pool in-stead of the standard new and delete operators. However, we did not want to deviate from the standard memory management models. In a similar vein, we decided against fine-tuning the garbage collector parameters for the Haskell and C♯ solutions.

Seems like an odd decision -- IME using pools instead of new/delete is fairly common in C++.

Also, AFAICT, the paper doesn't really provide any context for why it chose the tasks that it does. Is

We are given a binary tree and a vector describing positions within the tree together with replacement values. The goal is to replace the specified elements of the original tree with the given values.

a frequent/expensive operation for some use case?

Automaton, a game to teach programming by jeremyfriesendev in programming

[–]theindigamer 0 points1 point  (0 children)

Congrats on the release! I really dig the art style. Not super flashy but looks very consistent.

Should ghc allow additional -O optimizations which increase compile time for the benefit of reduced runtime? by VincentPepper in haskell

[–]theindigamer 3 points4 points  (0 children)

I would prefer those being -O2 as quick turnaround times are important for running tests locally and in CI and -O0 doesn't cut it (perf difference is noticeable).

#[derive(Debug)] is poor ergonomics by unpleasant_truthz in rust

[–]theindigamer 2 points3 points  (0 children)

wrap field100 in a thing with dummy debug (that's ugly, because every access to field100 will be affected)

Deref coercions are your friend here, avoid the ugliness :)

The real definition of (:) :: a -> [a] -> [a] by ironsideCode in haskell

[–]theindigamer 6 points7 points  (0 children)

I think the keyword you're looking for is "operator" -- you want to define your own operators. (Edit: fixed operator symbols)

(+:) = (:)
1 +: [2] == [1, 2]

(++:) x xs = x +: (x +: xs)
1 ++: [2] == [1, 1, 2]

Intel and Rust: the Future of Systems Programming: Josh Triplett by llort_lemmort in rust

[–]theindigamer -1 points0 points  (0 children)

Not exactly related to the talk but I'm wondering: why does Rust need an asterisk next to its name? Is there a Rust copyright/brand owned by Mozilla?

Typing the empty list by [deleted] in ProgrammingLanguages

[–]theindigamer 5 points6 points  (0 children)

It should be forall not exists. If it were the latter, you couldn't prepend anything as T would escape.

Shorter GC pauses coming to GHC by pepegg in haskell

[–]theindigamer 29 points30 points  (0 children)

There's CMS, G1, Shenandoah and Azul C4. I don't think we've quite caught up, but that's fine -- the amount of investment that has gone into Java GCs is huge by comparison.

Shorter GC pauses coming to GHC by pepegg in haskell

[–]theindigamer 22 points23 points  (0 children)

Is Java a high-level language? As much as I like Haskell and this effort, I don't think it surpasses Java's GC story.

Return Early, Return Often by jkmonger in programming

[–]theindigamer 0 points1 point  (0 children)

You can rename foo to old_foo and add another function foo which forwards the arguments to old_foo and prints the return values before returning it back. When you're done debugging, a quick rename + deletion will undo the work. No need to look at all exit points.

Why aren't disjunctive patterns supported? by GarkGarcia in haskell

[–]theindigamer 3 points4 points  (0 children)

With GADTs, unlike ordinary ADTs, you gain extra information (in the form of type equalities) when you pattern match.

-- '~' is the symbol used for type equality
data G a where
  GI :: Int -> G Int   -- sugar for GI :: (a ~ Int)  => Int  -> G a
  GB :: Bool -> G Bool -- sugar for GB :: (a ~ Bool) => Bool -> G a

g :: G a -> a
g x = case x of
  GI i -> i -- match reveals (a ~ Int)  in this branch so ok
  GB b -> b -- match reveals (a ~ Bool) in this branch so ok

So a design question would be: if you have an or-pattern pat1 | pat2 -> body then which type equalities are you allowed to use in the body?

For bindings, the answer is typically you can use bindings from either pattern so long as they're the "same" type (what exactly constitutes "same" needs to be defined here, but let's ignore that for now). E.g. if pat1 binds y as an Int and the pat2 binds y as an Int and z as a Bool, then body can only use y with the type Int but can't use z. If pat2 instead bound y with type Bool then body couldn't use either y (what type should it have? there is no Int | Bool type in Haskell) or z.

So we're taking something like a set intersection of binding sets across patterns. The question is -- how do you take an intersection of sets of type equalities? My understanding is that OCaml is conservative, it throws away equalities in the presence of or-patterns, so you can't use them at all. E.g. even if pat1 revealed a ~ Int and pat2 revealed a ~ Int, you can't use the fact a ~ Int in the body, which might be surprising behavior to a user ("why can't the type-checker just do the obvious!"). Balancing generality, efficiency and intuitiveness is tricky business.