HackerTrans
トップ新着トレンドコメント過去質問紹介求人

T-R

no profile record

コメント

T-R
·2 か月前·議論
No, the issue is if the first binding is on the same line as the `let`, you are required to write, e.g.:

    someValue = let f = 9
                    fo = 10
                    foo = 123
      in f+fo+foo
rather than:

    someValue = let f = 9
      fo = 10
      foo = 123
      in f+fo+foo
I think it used to be the case that it had to be indented past the `=` or the `let` even if it was't on the same line. Note also that `in` has to be indented past `someValue`, but doesn't need to be indented as far `let`.

This is fine:

    someValue = let
      f = 9
      fo = 10
      foo = 123
      in f+fo+foo
So, it is possible to land on sane indentation, but the parser is much pickier than, e.g., Python's off-sides rule, so it takes some trial and error for new users to find it, and it can be frustrating if you're just temporarily modifying an expression to quickly try something out.

I honestly think it would be less surprising if the parser just disallowed writing the first binding on the same line as the `let` entirely, treating it only as a block, but some people (bewilderingly) do seem to prefer to write their code with the excessive indentation (I'd imagine with editor support, rather than manually maintaining the spacing).
T-R
·2 か月前·議論
> inner expressions must be indented more

Not to support the parent comment, which I disagree with, but If you use multi-line let-bindings, those require that you indent not just more than the previous line, but as much as the first token after the let keyword on the previous line. It’s a very strange rule, all the more surprising because it’s inconsistent even with the rest of the language. It is totally avoidable if you, like I think most experienced haskellers do, just prefer ‘where’, but people more familiar with procedural code usually lean into using ‘let’ everywhere because it feels more familiar.

I think the strange indentation used to be required in more places - I vaguely remember running into it a lot more when I started with Haskell 20 years ago, but that was also just when I was new to the language. These days I just keep ‘let’ to a bare minimum, so it doesn’t bother me. One thing that made Elm frustrating was that it disallowed ‘where’ clauses, forcing you to deal with this weird edge case all the time.
T-R
·5 か月前·議論
I'm a huge fan of it - I was never able to get one while they were officially available, I only managed to rent one from Blockbuster a few times. I eventually picked one up on eBay in the early 2000's for $60, before they became collectors items.

The appeal is definitely somewhere between intrinsic and from the rarity - there's not just been no official way to play these games for the last 30 years, they've barely even gotten a passing mention, and even emualators have been few and far between. It was sad that Teleroboxer, Wario Land, and Mario Clash were basically lost to time.

The games definitely weren't up to to NES/SNES quality, but they were at least up to par with the average portable game at the time, which I think is what they were meant to be compared against. I see the Virtual Boy games as Game Boy games that suffered from the mechanics of the platform the platform they were released on. In that light, I think they compare pretty favorably to similar lesser-known games of the time; most WonderSwan games I've played, hell even a lot of Game Gear and Game Boy games, had much more serious gameplay issues, even though they were on hardware that was arguably less... challenging.
T-R
·6 か月前·議論
The article seems to assume readers are already familiar with the context, or maybe that they'll stop to read the original paper. For those who aren't familiar, Selective Applicative Functors were presented as part of the development of the Haxl library at Facebook (after they hired a series of prominent Haskellers like author of "Real World Haskell", Bryan O'Sullivan, and GHC Co-Developer Simon Marlow). Haxl is a batching framework for Haskell (to, e.g., solve the "N+1 database query problem"), which later inspired the various DataLoader libraries in other language ecosystems.

In Haskell, there's a lot of desire to be able to write effectful code as you normally would, but with different types to do things like restrict the available actions (algebraic effects) or do optimizations like batching. The approaches generally used for this (Free Monads) do this by producing a data structure kind of like an AST; Haskell's "do" notation transforms the sequential code into Monadic "bind" calls for your AST's type (like turning .then() into .flatMap() calls, if you're from Javascript), and then the AST can be manipulated before being interpreted/executed. This works, but it's fundamentally limited by the fact that the "bind" operation takes a callback to decide what to do next - a callback is arbitrary code - your "bind" implementation can't look into it to see what it might do, so there's no room to "look ahead" to do runtime optimization.

Another approach is to slide back to something less powerful than Moands, Applicative Functors, where the structure of the computation is known in advance, but the whole point of using Monads is that they can decide what to do next based on the runtime results of the previous operation - that they accept a callback - so by switching to Applicatives, by definition you're giving up the ability to make runtime choices like deciding not to run a query if the last one got no results.

Selective Functors were introduced as a middle ground - solidifying the possible decisions ahead of time, while still allowing decisions based on runtime information - for example, choosing from a set of pre-defined SQL queries, rather than just running a function that generates an arbitrary one.
T-R
·9 か月前·議論
Abstract Algebra, looked at through the lens of Programming, is kind of "the study of good library interface design", because it describes different ways things can be "composable", like composing functions `A -> B` and `B -> C`, or operators like `A <> A -> A`, or nestable containers `C<C<T>> -> C<T>`, with laws clearly specifying how to ensure they don't break/break expectations for users, optimizers, etc. Ways where your output is in some sense the same as your input, so you can break down problems, and don't need to use different functions for each step.

Category Theory's approach of "don't do any introspection on the elements of the set" led it to focus on some structures that turned out to be particularly common and useful (functors, natural transformations, lenses, monads, etc.). Learning these is like learning about a new interface/protocol/API you can use/implement - it lets you write less code, use out-of-the-box tools, makes your code more general, and people can know how to use it without reading as much documentation.

Focusing on these also suggests a generally useful way to approach problems/structuring your code - rather than immediately introspecting your input and picking away at it, instead think about the structual patterns of the computation, and how you could model parts of it as transformations between different data structures/instances of well-known patterns.

As a down-to-earth example, if you need to schedule a bunch of work with some dependencies, rather than diving into hacking out a while-loop with a stack, instead model it as a DAG, decide on an order to traverse it (transform to a list), and define an `execute` function (fold/reduce). This means just importing a graph library (or just programming to an interface that the graph library implements) instead of spending your day debugging. People generally associate FP with recursion, but the preferred approach is to factor out the control flow entirely; CT suggests doing that by breaking it down into transformations between data structures/representations. It's hugely powerful, though you can also imagine that someone who's never seen a DAG might now be confused why you're importing a graph library in your code for running async jobs.