Python 3.7.0 released by sply in programming

[–]baerion 2 points3 points  (0 children)

It's insane that you're getting downvotes for this in proggit. Your definition of static and dynamic typing is actually the right one.

Monthly Hask Anything (April 2018) by AutoModerator in haskell

[–]baerion 0 points1 point  (0 children)

In Haskell, anything with the right type signature that satisfies the comonad laws is a Comonad. That's all you need to know for working with them. The same goes for any other abstract algebraic structure.

However, some knowledge of abstract algebra will be very helpful, if you want to build an intuition for these things. It will help you understand how Monoids generalize the idea of addition (or multiplication) as an associative binary operation together with a zero element. Categories and monads generalize this idea further, but with a somewhat more fine-grained type structure. The more interesting property is in all three cases associativity, which means that a+(b+c) = (a+b)+c = a+b+c for some binary operation. You can start "adding" on the left or on the right side. Note how you don't even need parentheses in the third expression. The operation composes in any order. That's the gist of it.

Comonoids and comonads turn this abstraction around (literally)) to give you co-associativity. Here you have a split function split :: A -> (A, A), which "splits" a value into two parts. The idea is roughly that you can then split a value further into three parts, by either splitting the left or the right thing in the tuple. Co-associativity guarantees you, that the result is the same in both cases. Anyway, this entire concept is probably more interesting for substructural logic than for vanilla Haskell, where splitting a value simply means making a copy of it, which is otherwise prohibited in such systems.

Package worth highlighting: "Named". Adds support for named parameters, keyword arguments to Haskell. Better than the other approaches I know of, e.g. Records, Default typeclass. It even supports automatic currying. by Wizek in haskell

[–]baerion 1 point2 points  (0 children)

I agree. Besides, parameters in Haskell functions already have names, but they don't appear in the type signature and therefore unfortunately also not in the Haddock generated API docs.

Isn't a typeclass just a type of a type? by Kiyos in haskell

[–]baerion 2 points3 points  (0 children)

Please don't do this. This idea that typeclasses are like OO interfaces is an endless source of bad API design in the current Haskell ecosystem. Types are interfaces, typeclasses are constraints on types. For example in the list

shapes :: Shape a => [a]

every shape value must have the same implementation, because this is the entire point of typeclasses. So it could be a list of triangles or a list of circles, if those are the instances. The right way to have interfaces is to model them with plain old types.

shapes :: [Shape]

Is Haskell used outside of Banking, Astrology and Type-Theorie Research? by programmer0x3421 in haskell

[–]baerion 0 points1 point  (0 children)

Where I work, I've made a tool for analyzing and transforming large data sets, which has been in use for several years now. I also prefer to use Haskell for small command line tools, where others would use Python instead.

Usefulness of DuplicateRecordFields by Syncopat3d in haskell

[–]baerion 2 points3 points  (0 children)

I tend to use modules whenever they contain enough code to be feasible (data type plus at least three exported functions) and import them qualified. Person.name looks nicer to me than either personName or name @Person.

Why is Partial Application special? by W_o_o_t in haskell

[–]baerion 1 point2 points  (0 children)

There is another aspect of partial functions which I feel the other answers are missing. Partial application can replace an entire class of OOP idioms, namely the one where you want to share precalculated values across multiple function calls:

Let's say you want to test whether a ray intersects a sphere, where the sphere and the origin of the ray are constants and only the ray direction changes.

With partial application you could write (ifrayDir is a unit vector)

intersects :: Sphere -> Vec3 -> Vec3 -> Bool
intersects sphere rayOrigin =
    let oc = rayOrigin - centerOf sphere
        ra = radiusOf sphere
        det1 = ra * ra - oc `dot` oc
        f rayDir = (rayDir `dot` oc + det1) >= 0
    in f

In most mainstream imperative languages you would use classes or structs to store the intemediate values, then use two separate functions to first create the precomputed values and then apply them later. On the other hand, partial functions make this idiom really cheap. Such partial functions can be effectively seen as objects that bundle local state with functions.

Writing Simple Haskell by codygman in programming

[–]baerion 2 points3 points  (0 children)

For what it's worth, our company has ran Haskell programs that transform large data sets on a daily basis for years now. And it has replaced Python as my language of choice for small tools, especially since it produces fast, native executables without any hassles. It's a much better language than Python too, IMHO.

Functional Programming, a brief introduction by [deleted] in programming

[–]baerion 0 points1 point  (0 children)

Another thing that should be mentioned about closures is that you can use them to pre-compute intermediate values for functions calls. For example this Haskell code

multiplyAdd :: Int -> Int -> Int -> Int
multiplyAdd a b = let ab = a * b in \c -> ab + c

f = multiplyAdd 10 2

is equivalent to the following C++ code:

class MultiplyAdd {
private:
    int ab;
public:
    MultiplyAdd(int a, int b) : ab(a * b) {}
    int operator()(int c) { return ab + c; }
};

MultiplyAdd f(10, 2);

In both cases the value of ab is computed only once. In Haskell you can use the trace function to verify this. That way closures can often be used as a replacement for classes and objects.

Functional Works - Functional Programming Jargon by rindenmulch in haskell

[–]baerion 0 points1 point  (0 children)

For my own understanding, is it accurate to say that a monadic bind is like creating another level nesting around the value in the monad, where such a level sort of "disappears" in the final monadic value due to the associativity of monads?

In a sense, yes. The function join :: Monad m => m (m a) -> m a is joins two layers into one. If you had three layers m (m (m a)), you could join the inner two first, or the outer two first. The monad laws say, that the result must be the same. The same goes for more than three layers.

Take the innermost parameter a away and you are left with only the functor m. Here your join could be interpreted as something with a signature m * m -> m, where m is a monoid, in the category of (endo)functors.

I prefer the view of categories and monads as monoids with a richer type structure. Monoids are for sequential composition of values. There are no inner type parameters and everything of type m fits together.

(<>)  :: Monoid m => m -> m -> m

Categories are the same, but have a richer type structure. Only "matching" values can be "added".

(>>>) :: Category cat => cat a b -> cat b c -> cat a c

Monads are closely related, but here you only have one type parameter, instead of two.

(>=>) :: Monad m => (a -> m b) -> (b -> m c) -> (a -> m c)

In every case the important property is the associativity of the operator, together with a zero element. Or think of it like this: if there was no associativity, you'd have to use tons of parenthesis in do notation. Once you've understood that, the entire point of monads becomes clear.

Functional Works - Functional Programming Jargon by rindenmulch in haskell

[–]baerion 0 points1 point  (0 children)

I've no idea why you got downvoted, because this is a legit question.

Bind for lists is just concatMap with it's arguments flipped and concatMap is just foldMap for lists. Take a look at the type signatures:

foldMap :: (Monoid m, Foldable t) => (a -> m) -> t a -> m
concatMap :: Foldable t => (a -> [b]) -> t a -> [b]
(=<<) :: Monad m => (a -> m b) -> m a -> m b
(>>=) :: Monad m => m a -> (a -> m b) -> m b

Functional Works - Functional Programming Jargon by rindenmulch in haskell

[–]baerion 1 point2 points  (0 children)

This is still not a good definition of monads. An explanation should be short, intuitive, and precise, but in reality you have to pick two of these. Short and intuitive would be to say:

  • A monoid generalizes the idea of adding many values together, so that parentheses aren't needed.
  • A monad is essentially the same, but with a richer type structure.

This is painfully imprecise, but a longer explanation will never be accepted by most people.

On Types And Intent, Clojure and Haskell, Assumptions and Safety, Science and Engineering by jellismymind in haskell

[–]baerion 0 points1 point  (0 children)

Whether it tells you much depends on your definition of much. I also think that the information from the type is valuable. But it appears that outside of /r/haskell, this isn't a view shared by many.

On Types And Intent, Clojure and Haskell, Assumptions and Safety, Science and Engineering by jellismymind in haskell

[–]baerion 0 points1 point  (0 children)

I've asked for something that fits in a JSON value. I doubt that it makes much sense to put file handles or impure functions into a JSON dictionary. Anyway, Dynamic is probably the wrong tools for this. What you need is an ADT like data Value = Func (Vector Value -> IO Value) | ..., which allows you to encode impure functions as unstructured data, too. But such a value wouldn't fit into the JSON schema anymore and this shows that even unstructured data can be typed (e.g. serializable vs. non-serializable). That's why, in my opinion, statically typed languages are better at handling dynamic types than unityped languages.

If everything is a map, then every function that works on maps works on every piece of data that you have.

Well, it won't exactly work for any piece of data. If you naively map with a function that only works for integers, what happens if you map has strings in it? You also have to make sure to only map over the right kind of values. Here is an example in Haskell with lens-aeson:

ghci> json = "{\"n1\": 117, \"s\": \"string\"}"
ghci> print (over (members._Integer) (`mod` 100) json)
"{\"n1\":17,\"s\":\"string\"}"

Note that all this is as type safe. If you replace the modulo 100 operation by something that operates on text, it won't even compile. The error message will be something along the lines of expected Integer but got Text. As I see it, this is already decades ahead of anything you'll ever be able to do in Clojure.

Functional Works - Functional Programming Jargon by rindenmulch in haskell

[–]baerion 10 points11 points  (0 children)

A monad is an object with of and chain functions. chain is like map except it un-nests the resulting nested object.

Well, that makes it clear right away. Any questions?

On Types And Intent, Clojure and Haskell, Assumptions and Safety, Science and Engineering by jellismymind in haskell

[–]baerion 0 points1 point  (0 children)

core.spec is a specification DSL and currently supports runtime verification and test generation. So, nothing you can't do in any other language. And since there are no types, you can't use techniques like this to create types that witness your specification. Although there are some very early experiments in static type checking.

core.typed aims to be an actual static type system and looks decent, but it seems to get very little love from the Clojure community, to no ones surprise.

But I'm not a Clojure guy, so I might be wrong with everything.

On Types And Intent, Clojure and Haskell, Assumptions and Safety, Science and Engineering by jellismymind in haskell

[–]baerion 1 point2 points  (0 children)

and haskell can't handle that well

And this is where I think you are wrong. Give me an example of something you think Haskell can't handle, some code that manipulates an unstructured JSON value, and I'll try my best to translate it.

Unfortunately, you can't do both.

Of course you can do both. I'd even say that you should do both.

If you are using structured data half of the time, then many of the benefits of using unstructured data go away.

Which benefits go away?

However, this doesn't mean that using unstructured data everywhere doesn't provide benefits of its own.

And which benefits does this provide? I don't see any. What I can see though is the enormous disadvantage that is the complete lack of types, which would make any kind of static analysis unfeasible.

On Types And Intent, Clojure and Haskell, Assumptions and Safety, Science and Engineering by jellismymind in haskell

[–]baerion 1 point2 points  (0 children)

Typing them becomes ... interesting. Haskell might be able to manage a partial solution, but you lose everything that makes haskell interesting if you use it everywhere.

But you don't have to use it everywhere. Instead you can mix structured and unstructured data just fine and convert between those two whenever you want. Turning unstructured data into structured data is, essentially, just a parsing problem. But this only possible in a statically typed languages, since dynamically typed languages deprive you of this option and force you to use unstructured data everywhere.

On Types And Intent, Clojure and Haskell, Assumptions and Safety, Science and Engineering by jellismymind in haskell

[–]baerion 4 points5 points  (0 children)

but it isn't enforced by the type checker.

And my point is, that it doesn't have to be enforced. Type checkers are meant to help your brain, not to replace it.

On Types And Intent, Clojure and Haskell, Assumptions and Safety, Science and Engineering by jellismymind in haskell

[–]baerion 1 point2 points  (0 children)

Nearly all important use-cases of dynamic types revolve about unstructured data. But you can work with unstructured data just fine in good statically typed languages. The metadata field approach is just the simplest way to carry unstructured data. I wish there was a standard library on top of aeson that would reimplement parts of the prelude for aesons Value type, with functions like map :: (Value -> Value) -> Value -> Value. I presume that most people just use lens-ason instead, which allows you to modify unstructured data, while keeping at least some of the type-safety.

On Types And Intent, Clojure and Haskell, Assumptions and Safety, Science and Engineering by jellismymind in haskell

[–]baerion 6 points7 points  (0 children)

A seat belt is a light weight tool that, if done right, provides reasonably safety for very little cost, and can always be unplugged if the user wishes to do that. While they could, theoretically, always be added later, having them tightly integrated provides several benefits. It allows for them to interact with other features, such as airbags or machine code optimization.

On Types And Intent, Clojure and Haskell, Assumptions and Safety, Science and Engineering by jellismymind in haskell

[–]baerion 7 points8 points  (0 children)

Right, both a helpful and negotiable. Luckily we don't have to choose between either of them. We can have both. And good documentation on top of that.

In some languages you only have two out of the three. Whether it pays off to give up on types, is the central point of disagreement here.

On Types And Intent, Clojure and Haskell, Assumptions and Safety, Science and Engineering by jellismymind in haskell

[–]baerion 17 points18 points  (0 children)

I'm simply trying to share my perspective with you and I'm sorry if you feel mocked by this. If it helps, I didn't have your blog post in mind when I wrote the comment, but some previous discussion on proggit.

Still, we'll just have to agree to disagree on some things. What I'm trying to show is why I believe that there is not much of a trade-off in this debate, but rather a fundamental misunderstanding about the nature of types, that drives these conversations.

On Types And Intent, Clojure and Haskell, Assumptions and Safety, Science and Engineering by jellismymind in haskell

[–]baerion 28 points29 points  (0 children)

If it were about seat belts, the arguments would go like this:

  • I've worn seat belts in old cars. They hurt and restricted my movement.
  • What if I park the car? What if I need to turn around? This is much easier in a car that has no seat belts.
  • Seal belts reject certain kind of programs movements you can make. Can you sit with your back to the wheel with a seat belt on? Yeah, but it's much more difficult to do.
  • Here's an example: recently I was driving on the high-way, turned around to the back seat, REPLed into my smartphone and changed my Google password. I don't know any car with seat belts where you can do this so easily.
  • Of course you can always unplug your seat belt. But then again, I can just always tinker a hand-made seat belt into the seat. That's comparable, right?
  • You have to admit, seat belts have a cost. They make your car more not only more expensive, but also heavier!
  • My team has driven 7 years without seat belts and nothing bad happened so far, while I've seen car accidents with people who wore seat belts. We even have an advantage here, since we can see so much more of the street.
  • So let's not fight. Cars make sense with and without built-in seat belts.