(define make-tree cons) - is very natural thing - it is called synonym - just another word for the same thing in a new context. Moreover, this is a better candidate for a proper way.)
separation of effects from pure functions - this I cannot understand. Aren't these function pure?
And another part of "properness" - each node of a tree is a tree itself. Sometimes less types is more.)
Well, I'm old-fashioned, but I think that being able to put any kind of data into a list is the strength, not weakness of a language, and that user-defined ADTs and compound data-structures needs no explicit typing, they are nothing but conventions.
Types provide one important thing: static guarantees. Compile-time type checking can only work with strong, expressive data types, and ADTs provide a very good way of enforcing that. (Together with Haskell's `newtype` keyword, which really is nothing more than a convention, if you will, a different way of expressing a type synonym, but with the static guarantee that when you make up a `newtype Name = Name Text`, you cannot use `Name` wherever you can use `Text`, but you can access the `Text` `Name` is a wrapper for easily.
This makes it much easier for your intent to be expressed directly in the code, namely in the data types. Just compare:
makePerson :: Name -> Age -> Gender -> Address -> Person
makePerson' :: Text -> Int -> Text -> Text -> Person
separation of effects from pure functions - this I cannot understand. Aren't these function pure?
And another part of "properness" - each node of a tree is a tree itself. Sometimes less types is more.)
Well, I'm old-fashioned, but I think that being able to put any kind of data into a list is the strength, not weakness of a language, and that user-defined ADTs and compound data-structures needs no explicit typing, they are nothing but conventions.