HN user

mrgriffin

189 karma
Posts0
Comments57
View on HN
No posts found.

Steelmanning this decision:

I would guess for the use-case of "I have a C project and I want to run it in Fil-C" the ability for this to be a warning + run-time panic is very helpful for quickly getting started. Reminds me of GHC's -fdefer-type-errors.

I agree that I wouldn't want to deploy a program where those panics are reachable*, but it's still handy for local development and/or maybe the developer knows they aren't reachable.

I haven't checked, but I'd guess there's a warning and a -Werror -style flag to opt-in to having a hard error for unsafe assembly?

* Obviously a panic is better than not. But guaranteed safeness is better than either of those.

I'm sure Bill understands what I'm about to say, but as a person on team "require explicit initializations" I think the mitigations I would be looking at are:

1. Only require that the programmer prove the variable is initialized before it's read. Riffing on Bill's example:

    Object *x; // Fine
    if (is_foo) {
      x = &foo;
    } else {
      x = &bar;
    }
    x->a // Fine, was initialized by the time it was used.
Of course this is still a trade-off, your compiler has to work harder to prove that all paths that flow to each use are definitely-initialized. When you have initialization functions you now need to have type specifiers like 'in', 'out', and 'in/out' so that 'out' can take a pointer to uninitialized data, or something like MaybeUninit. This handles this example from Bill:
    Foo f;
    Bar b;
    grab_data(&f, &b); // Fine if 'grab_data(out Foo *, out Bar *)'.
2. Something like Rust's MaybeUninit to explicitly opt-in to wanting to deal with possibly-uninitialized data. Obviously also a trade-off, especially if you want to force all the maybe uninitialized stuff to be in an 'unsafe' block.

Making all registers caller-saved around context switches is a neat insight, it's intuitive how that could potentially lead to needing fewer instructions to execute when switching contexts.

I haven't yet digested exactly how the stacks are made to work. I think fig 8 should be obvious, but I'm getting stuck on picturing how the system guarantees that frame 1 can't grow into frame 2.

EDIT: Ah, I think I understand. It seems that they're taking advantage of a large virtual address space to give each stack loads of space, rather than them being contiguous like I assumed from fig 8.

As modern 64-bit address-space is big enough for the allocation of many large continuous stacks [...]

`foo :: Semigroup a, Traversable t => t a -> a` I already know that whatever is passed to this function can be traversed and have it's sum computed. It's impossible to pass something which doesn't satisfy both of those constraints as this is a specification given at the type level which is checked at compile-time.

To add to your point, I don't think foo can even be implemented (more accurately: is not total) because neither `Semigroup a` or `Traversable t` guarantee a way to get an `a`.

I think you'd need either `Monoid a` which has `mempty`, or `(Foldable1 t, Traversable t)` which guarantees that there's at least one `a` available.

Would you expect IS_CONST to evaluate to the constant? With a name like that I would expect it to evaluate to true/false.

C here is asserting that the value inside is a constant and then evaluating to that constant.

One very frustration aspect is that there is basically no real documentation

I looked at DRM/KMS briefly earlier in the year and this is what made me abandon it in the end. Can you recommend any sources of information?

The atomic API and "test only commit" both sound really useful.

doesn't run against the grain of the entire language

Not an expert, but my gut says maybe it runs against zero values? As in, "what's the zero value for a non-nullable reference?" Maybe the answer is something like "you can only use this type for parameters", but that seems very limiting.

I think the person you're responding to is agreeing with you that you should optimize for languages other than English rather than for people naming a label "Bin" and then switching to the UK locale.

I wouldn't do it, but this works perfectly fine. The rule is "use tabs to indent blocks of code, use spaces to align within those blocks".

  <TAB><TAB>function name(arg1, arg2,
  <TAB><TAB>              arg3)

What about implementing something like map for lists? You know literally nothing about the values and the function except for their types.

    map :: (a -> b) -> [a] -> [b]
    map f [] = []
    map f (a:as) = f a : map f as
Personally I don't see any reason to use longer names than f, a, and as because you can't add any additional information.

You might say "well I don't write code like that", and I don't have any answer for you. But I find I write a fair amount of generic code.

I'd probably think of Bool as being isomorphic to Maybe (), which would mean your map implementation looks a bit like:

    map :: Bool -> (() -> b) -> Maybe b
    map True f = Just (f ())
    map False _ = Nothing
Or, in Haskell because you've got laziness you could go with:
    map :: Bool -> b -> Maybe b
Which already exists as a combination of Control.Monad.guard and Data.Functor.(<$).
    map tf b = b <$ guard tf

I've gotta say, if I was a third party to the exchange that involved your suggested message I would think the senior developer was a pompous and condescending ass. In the UK we (or at least I) wouldn't consider what you wrote to be a polite response.

I had meant to restrict my weight lifting comments to the kind of weight lifting that goes on in gyms (what was being discussed upthread). And I have to confess I don't know much about those sports you named, but do the participants really interact with each other like they would in, say basketball or soccer? Maybe there really is a rule like "there are a finite number of weights, and each can be lifted by at most one person", which would be pretty similar to "there's only one ball, therefore only the player with the ball can score"? And therefore being paired with someone who vastly outclasses you wrt lifting weights will effectively render you unable to participate (by taking all the weights you can lift, similar to taking 100% of the possessions of the ball).

From the article:

multiplied does not: it starts off 0, and every time it is multiplied via multiplied = multiplied * count it remains 0.

The article then goes on to use that fact for some optimizations.

You might be right, it's been a long time for me too. But if it can define new classes, then I'd expect that the code for those classes' methods would also be in the pickled format, at which point there's no particular reason you couldn't deserialize it in Haskell too... with some caveats about either needing to know what types those functions should have, or needing to build a run-time representation of those types so that they can be checked when called, or (hopefully!) crashing if you're okay with just unsafeCoercing things around.

I'm pretty confident that you could write something that was equivalent to all the useful `pickle` calls. By that I mean you'll need to know which operations you'll want to do on your unpickled object:

  readAny :: forall c r. [TypeWithReadAnd c] -> String -> (forall a. c a => a -> r) -> Maybe r
  readAny types string handle 
I think it's fair to say "hey, pickle doesn't require me to list all my types explicitly", but on the other hand, it's not like pickle can conjure those types out of thin air--it considers only the types that are defined in your program.

Here's an example that uses Read as the serialization format and only deals with Int, Char and String; but hopefully you can imagine that I could replace the use of read with a per-type function that deserializes from a byte string or whatever.

https://repl.it/@mrgriffin/unpickle

I think (but cannot guarantee) there's nothing much stopping you writing template Haskell to construct valid values at compile time if you want. It's just that most of the time you're (or at least I am) happy to use "unsafe" functions because the verification is simple enough to do in your head, and the other 99% of the time I'm creating values it's from run-time data.

Not to sound like a pedant, but the small spelling mistakes would scare me away if I were a potential customer.

but even the most beautiful code is useless if it takes to long to write!

"too long"

Tansparent

"transparent"

visualiase

"visualize"

There are probably others, I only skimmed.

Anyway, the visualization of connecting inputs to outputs is pretty cool and I hope that it's intuitive enough to bring simple programming to more people.

I don't write brackets where the condition and body fit on one line, and do otherwise. e.g. like these:

  if (ptr == NULL) return NULL;
  for (int i = 0; i < n; ++i) dst[i] = src[i];
I particularly like it for guards at the beginning of a function, because they end up being less visually heavy, thus drawing my attention to the (presumably more business-logic-y) other lines of code.

It's somewhat like how Ruby lets you write your control structure at the end of a statement to have it only apply to that statement, e.g.

  return nil if ptr.nil?
And I find this style looks different enough to multiple statement control structures that I don't make the mistake of adding an additional statement without also adding brackets.

Of course this is a comment about formatting code, so YMMV.

One solution to ensuring that the order is preserved (that I've actually used in production Haskell code where this was a real risk) is to use a heterogeneous list instead of a Vec.

  rzipWith :: (forall a. f a -> g a -> h a) -> Rec f as -> Rec g as -> Rec h as
With this type I don't think you'd be able to rearrange the elements, but you do have to pay the cost of using Const everywhere, as in you can specialize approximately my function as follows (handwavy, won't compile):
  zipWith :: (a -> b -> c) -> Vec n a -> Vec n b -> Vec n c
  zipWith f as bs = recToVec (rzipWith f' as' bs') where
    as' = vecToRec (fmap Const as)
    bs' = vecToRec (fmap Const bs)
    f' (Const a) (Const b) = Const (f a b)

  vecToRec :: Vec n (f a) -> Rec f (Replicate n a)
  recToVec :: Rec (Const a) as -> Vec (Length as) a
Rec is defined in Vinyl: https://hackage.haskell.org/package/vinyl-0.11.0/docs/Data-V...

there are equally disastrous bugs caused by intent - i.e. the programmer had no idea the code could fail.

At least some of these sorts of bugs (depending on how you define "fail") are captured even in Haskell today by things like Maybe. In fact, if Haskell was total (like Idris) you wouldn't even be able to write a function that searches a list for an element with this type:

    find :: (a -> Bool) -> [a] -> a
Because there's no way for you to magic an a out of nowhere in the empty list case (whereas laziness lets you use bottom). You could however write a buggy find over known non-empty lists because you could always return one of the known elements, types aren't a panacea.

Dependent types go further and let you guarantee things like "zip only compiles for two lists of exactly the same length" which can be helpful in some circumstances (specifically in Haskell we use things like zip [0..] too often to want to change the function literally called zip, but you could imagine another name, perhaps zipExact as in the Safe library).

I suspect you probably already know this and meant a different kind of failure, such as mathematically invalid (which you could probably encode via a sufficiently powerful type system), or a simple misunderstanding of the requirements (which you—as the misunderstander—obviously can't).

I hope it's not literally to prevent people reinventing these simple data structures.

The ecosystem exerts a lot of pressure on developers to not reinvent fundamental things, in Haskell I practically never reach for a StrictTuple or StrictEither even when I'd like strictness specifically because they are clunkier and less well integrated with other libraries.

Scala is the only language that comes to mind with this issue (scalaz vs Cats) and my impression is that even they have mostly converged.

I would say Go has much stronger idioms than either of these languages, and wouldn't need to be afraid of people inventing their own fundamental types provided they're included in the standard library.

As a professional Haskeller who solves numerical computation problems I wouldn't call ST an escape hatch. You're right that I can't think of a "pure" way to compute a histogram, but I doubt anyone uses Haskell specifically to prevent all mutations but rather to carefully control where mutation can occur[1]. I'd wager referential transparency is "good enough purity" for almost all tastes.

[1] Of course we have plenty of other reasons to use it, but I digress.

Not OP, but my understanding is that it's a common approach in Idris. Here's the creator of the language deriving the implementation of zipWith without writing any code: https://www.youtube.com/watch?v=X36ye-1x_HQ&t=1274

Some other examples include:

- Haskell: https://hackage.haskell.org/package/ghc-justdoit

- Scala: https://github.com/TypeChecked/alphabet-soup

- Agda: https://youtu.be/3U3lV5VPmOU?t=3159

I was under the impression that justDoIt comes from Scala, but I can't find a reference for that.