Hacker News new | past | comments | ask | show | jobs | submit | Iceland_jack's comments login

lift = sequenceA @[] @(a ->)


Did you do that by hand? I guessed you used the classic `pointfree` program, bit that gave

    lift = fix ((`ap` tail) . (. head) . flip ((.) . liftM2 (:)))
As far as I know it's not possible to get this functionality in Haskell even with clever instance magic, but I'd love to be proved wrong.


> As far as I know it's not possible to get this functionality in Haskell even with clever instance magic

It is possible to fill in basic function bodies based on their type, using ghc-justdoit (https://hackage.haskell.org/package/ghc-justdoit). That's maybe not what you meant, if you are looking for integrating pointfree into Haskell it can be added to ghci or your development environment.

    foo :: ((a -> r) -> r) -> (a -> ((b -> r) -> r)) -> ((b -> r) -> r)
    foo = (…)
In this case I wrote it because I knew about the pattern. Your lift definition is just ($)

      flip \a -> ($ a)
    = flip (&)
    = flip (flip ($))
    = ($)


Maybe you meant to write

    (??) :: Functor f => f (a -> b) -> a -> f b
    funs ?? a = fmap ($ a) funs
from lens: https://hackage.haskell.org/package/lens-5.3.2/docs/Control-...

This is valid definition of lift along a different, less interesting but more general axis.

    lift = (??) @[]


    lift = flip $ \a -> ($a)



Definitely interesting, but it's flat-out wrong about the limitations of `make`.

In particular, the `release.txt` task is trivial by adding a dummy rule to generate and include dependencies; see https://www.gnu.org/software/make/manual/html_node/Remaking-... (be sure to add empty rules to handle the case of deleted dynamic dependencies). You can use hashes instead of file modification times by adding a different kind of dummy rule. The only downside is that you have to think about the performance a little.

I imagine it's possible for a project to have some kind of dynamic dependencies that GNU make can't handle, but I dare say that any such dependency tree is hard to understand for humans too, and thus should be avoided regardless. By contrast, in many other build tools it is impossible to handle some of the things that are trivial in `make`.

(if you're not using GNU make, you are the problem; do not blame `make`)


I guess you aren't keen on Java then? Complex dynamic dependency graphs aren't difficult for humans to handle or many build tools other than make.


I'm not keen on Java for other reasons. The fact that a single .java file can generate multiple .class files is annoying but not something tools can't handle (it's actually similar to .h files for C/C++ - remember, we only need the complete dependency graph for the rebuild, not the current build).

The main part that's difficult for humans is if there's a non-public class at top level rather than nested (I forget all the Java-specific terminology for the various kinds of class nesting).


    > I guess you aren't keen on Java then?
Can you explain more? I don't follow.


Java dependencies are too complicated for make. See https://www.oreilly.com/library/view/managing-projects-with/...


Haskell focuses on both, but prioritizes research. I have had several Haskell jobs and highly recommend it for the personal enjoyment.


Any function matching the type

    St -> (a, St)
can viewed as `State St' parameterized over `a'. There are several behaviors that organize such functions, such as Monad which "overloads the semicolon" to work on stateful computations. Another behavior implements a stateful interface: `MonadState St'.

    get :: St -> (St, St)
    put :: St -> (St -> ((), St))
With type classes in Haskell, behaviour is type-directed so in order to reap the benefits we declare a new type.

    {-# Language DerivingVia #-}
    {-# Language GADTSyntax  #-}

    -- get :: My St
    -- put :: St -> My ()
    newtype My a where
      My :: (St -> (a, St)) -> My a
      deriving (Functor, Applicative, Monad, MonadState St, MonadFix)
      via State St


Is there are correct way around?

    St -> (a, St)
vs

    St -> (St, a)
I've found both in the wild and it can be very confusing!


The order doesn’t matter as long as you’re consistent, but Control.Monad.State uses the first, and you’ll have a bad time (in Haskell anyway) if you’re not compatible with that.


It’s the same thing. Order doesn’t matter in the tuple. Just use a bind which is in line with the signature of the type you want to use.


Applicative is n-ary lifting, Functor is a special unary case of Applicative:

    liftA0 :: Applicative f => (a)                -> (f a)
    liftF1 ::     Functor f => (a -> b)           -> (f a -> f b)
    liftA2 :: Applicative f => (a -> b -> c)      -> (f a -> f b -> f c)
    liftA3 :: Applicative f => (a -> b -> c -> d) -> (f a -> f b -> f c -> f d)
    .. where
  liftA0 = pure
  liftF1 = fmap


Monads don't have anything to do with laziness but historically the need for them arose because of laziness. It's the first thing explained in the introduction of Tackling the Awkward Squad:

"Call-by-need (or lazy) languages, such as Haskell, wear a hair shirt because their evaluation order is deliberately unspecified. Suppose that we were to extend Haskell by adding side-effecting “functions” such as printChar. Now consider this list

  xs = [printChar 'a', printChar 'b']
(The square brackets and commas denote a list in Haskell.) What on earth might this mean? In SML, evaluating this binding would print 'a' followed by 'b'. But in Haskell, the calls to printChar will only be executed if the elements of the list are evaluated. For example, if the only use of xs is in the call (length xs), then nothing at all will be printed, because length does not touch the elements of the list.

The bottom line is that laziness and side effects are, from a practical point of view, incompatible. If you want to use a lazy language, it pretty much has to be a purely functional language; if you want to use side effects, you had better use a strict language.

For a long time this situation was rather embarrassing for the lazy community: even the input/output story for purely-functional languages was weak and unconvincing, let alone error recovery, concurrency, etc. Over the last few years, a surprising solution has emerged: the monad. I say “surprising” because anything with as exotic a name as “monad” — derived from category theory, one of the most abstract branches of mathematics — is unlikely to be very useful to red-blooded programmers. But one of the joys of functional programming is the way in which apparently-exotic theory can have a direct and practical application, and the monadic story is a good example. Using monads we have found how to structure programs that perform input/output so that we can, in effect, do imperative programming where that is what we want, and only where we want. Indeed, the IO monad is the unifying theme of these notes."

https://www.microsoft.com/en-us/research/wp-content/uploads/...


In Haskell behavour is type-directed (type classes) and they describe both.


What does that mean?


I met Sam Derbyshire at ZuriHac who told me all the difficult architectural work had been done for SIMD support.

+ https://gitlab.haskell.org/ghc/ghc/-/issues/7741

It might make it for GHC 9.12 (for 128 bit vectors only, and mostly floating-point operations unless other people come in and contribute).

The patch is at:

+ https://gitlab.haskell.org/ghc/ghc/-/merge_requests/12860


Thanks for the info!


I write this in my Haskell day job every day.


Consider applying for YC's W25 batch! Applications are open till Nov 12.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: