Yet another thread that probably belongs on scala-debate.
AND to answer with my opinion : I think using meaningful type parameter names can make sense, but there's a balance to be had.
This comes from thinking about the alleged complexity of Scala (others' arguments have convinced me it is a real problem in the corporate world), my own experiences as I start to penetrate a little bit of the "inner core" of Scala, and most of all, trying to be productive and ask, "Are there simple ways to mitigate the complexity problem?" (Assuming you believe it is a problem.)First, consider the following method:def someMeaningfulName(x: Int, y: String, z: Long) {...}I don't know about you, but I have never worked in an environment where this would be considered acceptable code (unless x, y, and z actually had special meaning in the problem space). I think it's a given that good software engineering demands meaningful variable names, both internal and visible.Now consider the following type signature:flatMap [B, That] (f: (A) ⇒ GenTraversableOnce[B])(implicit bf: CanBuildFrom[List[A], B, That]): That
There are three type parameters, A, B, and That. None are meaningful. Due to the fortunate inclusion of f: in the type, it's fairly easy to figure out the meanings of A and B. We can finally figure out the meaning of That when we reach the end of the line, at which point we need to reread the entire line to put everything together. (OK, you may not. I do, and I submit that most programmers will need to do so once or more.)
In fairness, there was a valid reason for using one-char type param back when type params were new; the declarations were simply (usually container classes), and the one-char rule made it obvious what was a type param and what was an actual type. Note the above def'n has already violated the one-char rule, without really adding value.How about thisflatMap [resultElementType, resultType] (f: (inputElementType) ⇒ GenTraversableOnce[resultElementType])(implicit bf: CanBuildFrom[List[inputElementType], resultElementType, resultType]): resultType
This is much more verbose, but to someone unaccustomed to Scala collections, it is also much clearer. I've taken the liberty of using initial lowercase to denote a parameter rather than an actual type; I believe this is illegal, but I simply wanted to show how things could be made more readable.If you're an experienced Scala user, this may not seem a big deal, just extra characters. Consider the process of reading the type signature from left to right, understanding as we go:
flatMap [B, That]
vs.flatMap [resultElementType, resultType]
The second has immediate meaning. The first does not.The example I chose was mid-level in complexity--it is as the top of what a Scala novice should encounter, but nowhere near as complex as one might encounter in Scala internals or scalaz.I believe that encouraging meaninful type parameter names would help the perceived "Scala complexity" problem in two ways. First, it makes the more complex "beginner's" Scala signatures more immediately understandable to beginners. Second, it make intellectually sophisticated Scala code more acceptable to the mainstream by making it more readable.OK, that's it. Discuss :-)Thanks,Ken
This is exactly the mistake I refer to. Now define your operations on
the data type. What do those operations (the important bits) say about
nodes and edges? Exactly, nothing, nothing at all -- now you're stuck in
a cognitive quagmire. Please allow me to politely decline your invitation.
--
Tony Morris
http://tmorris.net/
On 17/11/11 22:56, Matthew Pocock wrote:
> trait Graph[TheNodeType, TheEdgeType]
This is exactly the mistake I refer to. Now define your operations on
the data type. What do those operations (the important bits) say about
nodes and edges?
Exactly, nothing, nothing at all
-- now you're stuck in
a cognitive quagmire. Please allow me to politely decline your invitation.
[I'm now subscribed to scala-debate - sorry if you got this twice]On 17 November 2011 13:13, Tony Morris <tonym...@gmail.com> wrote:
On 17/11/11 22:56, Matthew Pocock wrote:> trait Graph[TheNodeType, TheEdgeType]
On 17/11/2011 13:51, Josh Suereth wrote:
Yet another thread that probably belongs on scala-debate.
AND to answer with my opinion : I think using meaningful type parameter names can make
sense, but there's a balance to be had.
Yes.
When I discovered Scala, I was disconcerted, not to say "shocked", by these one-letter type names. Looked like those old C programs where developers valued conciseness over expressiveness. You know, lines like: if (lh->v.u.s.aux == v->u.s.info)
On the other hand, there can be some confusion between real types (classes, etc.) and abstract types (those used in collections, not knowing in advance what they would be); perhaps even potential clashes (?).
--
Philippe Lhoste
-- (near) Paris -- France
-- http://Phi.Lho.free.fr
-- -- -- -- -- -- -- -- -- -- -- -- -- --
Except you just killed Node and Edge as trait/class names. At the very
least, I'd call them ANode and AnEdge.
--
Daniel C. Sobral
I travel to the future all the time.
--
Daniel C. Sobral
I travel to the future all the time.
Won't show up on Scaladoc, so what's the point? Nope, I still prefer
ANode and AnEdge. :-)
You should see that Uncle Bob has to say about prefixing interfaces
with I. Pretty funny! :-)
http://www.scala-lang.org/api/current/index.html#scala.collection.Map
Peace. Michael
2011/11/17 Daniel Sobral <dcso...@gmail.com>:
Yes.... Wouldn't Map[K, V] or Map[Key, Value] be more self-explanatory? Peace.
On 17 November 2011 12:11, Tony Morris <tonym...@gmail.com> wrote:>It is extremely important to pick meaningless names. The moment you
project meaning is the moment you start making all those mistakes that
we so often see.Many times the types are not devoid of meaning. Consider:trait Graph[A, B]
trait Graph[N, E]
trait Graph[Node, Edge]
trait Graph[TheNodeType, TheEdgeType]
+1
--
Seth Tisue | Northwestern University | http://tisue.net
lead developer, NetLogo: http://ccl.northwestern.edu/netlogo/
Agreed.
I find the single letter convention actually makes the use of generics
more obvious to me and easier to reason about. If I had to figure out
whether everything that looked like "Node" was a type parameter or a
concrete type I would be sad. [1]
-- Erik
[1] Before Scala I had no background in languages that had type
parameterization, so (as far as I can tell) I don't have an earlier
attachment to this style.
Write a few of your graph operations. Use the type parameters as if they were nodes or edges. Notice how you can't. The proposal is akin to projecting meaning onto universally quantified variables of a logical proposition. We say, paraphrased:
For all c such that c is an element of the set Cars...
Notice how we call the variable c, not car or any other particular meaning. Doing so would degrade readability to the extent that it may distract from the fact that this variable should contain no further context than its existence. The constraint of set membership is simply an application to an operation, no different to a list sort operation requiring ordering on its elements.
It is extremely important that type variable names contain no context (that is, call it what you want but I won't be projecting an imaginary context onto it). I am only motivated to point this out because one day someone is going to bring this point up IRL and I am going to have to deal with it. I don't want to; this elusive insight that I am apparently missing according to "the naming guys" is repeatedly shown to be a failed thesis and now it is crawling into the least welcome of places (type variables).
Please modify the thesis for practical purposes.
2011/11/17 Matthew Pocock <turingate...@gmail.com>Yes.... Wouldn't Map[K, V] or Map[Key, Value] be more self-explanatory? Peace.
There will always be people to not catch [K,V] as [Key, Value], and others to not catch what is Key or Value because they just don't know what a Map is (let's say the hereby example is easier than the general case).
Do you not think I have worked with people who were once "naming guys"?
They are wrong, so they tried every trick they had, like you are doing.
They're not "naming guys" anymore.
Either demonstrate the point (an appeal to normality/abnormality
exclusion is fallacious -- surely this is obvious) or work out that
you're making a mistake with adverse practical implications. All you've
got to do is use your Node/Edge type variables as if they were nodes or
edges. Let me know when you have achieved this.
>>> Matthew
>>>
>>>
>>>> --
>>>> Tony Morris
>>>> http://tmorris.net/
>>>>
>>>>
>>>>
>>>
>>> --
>>> Dr Matthew Pocock
>>> Integrative Bioinformatics Group, School of Computing Science, Newcastle
>>> University
>>> mailto: turingate...@gmail.com
>>> gchat: turingate...@gmail.com
>>> msn: matthew...@yahoo.co.uk
>>> irc.freenode.net: drdozer
>>> skype: matthew.pocock
>>> tel: (0191) 2566550
>>> mob: +447535664143
>>>
>>>
>
--
Tony Morris
http://tmorris.net/
Yet another thread that probably belongs on scala-debate.
AND to answer with my opinion : I think using meaningful type parameter names can make sense, but there's a balance to be had.
1) Am I proposing an EASY solution (such as simply lengthening type variable names).
2) Does this address a serious perceived problem with Scala (such as the corporate perception that Scala is "too complex")
3) Am I proposing something that is closed-ended,in the sense that it will not devolve into (somewhat :-) ) infinite debates over detail. In other words, am I presenting people with an easy choice? The goal of this last point is to avoid flame wars and indefinite posts back to the same subject. Such topics should definitely be in scala-debate.
4) Does this address something that I, still a Scala novice, have found to be a genuine problem and believe would be a genuine problem in the broader world. I admit this is a subjective judgement, I do the best I can :-).
I am using myself as a proxy for a "new Scala user". I want Scala to succeed, so I believe that Scala problems that are SERIOUS problems for new users should be discussed in a mainstream group such as scala-user, not in a peripheral group such as scala-debate.
Are you seriously expecting me to buy this? That I, and others who
aspire to get practical work done, are not "normal programmers", while
you, and your buddies are? Is this how you justify your thesis to
yourself? Sorry, but it's pretty transparent to me.
Either demonstrate the point (an appeal to normality/abnormality
exclusion is fallacious -- surely this is obvious) or work out that
you're making a mistake with adverse practical implications.
All you've
got to do is use your Node/Edge type variables as if they were nodes or
edges. Let me know when you have achieved this.
On 17 November 2011 19:59, Tony Morris <tonym...@gmail.com> wrote:
Are you seriously expecting me to buy this? That I, and others who
aspire to get practical work done, are not "normal programmers", while
you, and your buddies are? Is this how you justify your thesis to
yourself? Sorry, but it's pretty transparent to me.
You're self-evidently not a normal programmer. You care about types and correctness, but normal programmers care about writing the code and think about types afterwards, if at all. I am also not a normal programmer - I care a lot about performance, correctness and scoping/visibility. I've worked with and taught lots of normal programmers, and neither of us are that.
Either demonstrate the point (an appeal to normality/abnormality
exclusion is fallacious -- surely this is obvious) or work out that
you're making a mistake with adverse practical implications.
You've demonstrated no adverse practical implications of using informative names for type parameters - you've just appealed to their existence. Making bare assertions like this is fallacious. Can you show me, using Graph[N, E] as an example where we make incorrect assumptions about it if we think of N as Node and E as Edge, instead of an anonymous [A, B]?
All you've
got to do is use your Node/Edge type variables as if they were nodes or
edges. Let me know when you have achieved this.
They become nodes or edges by virtue of their role in the graph. I'm not sure what other possible meaning there is to a node/edge in this context. It's a role they play, not something intrinsic to them. This role still needs naming, and the type parameter is the only place to name it.
On 17 November 2011 23:11, Tony Morris <tonym...@gmail.com> wrote:
You have named things Node/Edge. This is not a big deal *except you believe that your type parameters have something to do with graph nodes and edges*.
I honestly have no idea what you're driving at. def nodes: Set[Node] is semantically meaningful for a graph as Graph[Node, Edge]. def nodes: Set[Edge] is wrong. def incidentEdges(n: Node): Set[Edge] means something, and the type names are meaningful. The operations *define* what it is to be a graph node and edge. It's a role, not a type. There's no operation to perform on Node or Edge, only operations on Graph[Node, Edge].
You are absolutely wrong. It is not much of a leap to work out what adverse practical implications will happen from there -- you've just got to work out that you are wrong.
Show me the mistake I'm encouraged to make by thinking of graphs over nodes and edges modelled by the type parameters Node and Edge that I would not make naming these parametrized types A, B.
You have named things Node/Edge. This is not a big deal *except you believe that your type parameters have something to do with graph nodes and edges*.
You are absolutely wrong. It is not much of a leap to work out what adverse practical implications will happen from there -- you've just got to work out that you are wrong.
-- Tony Morris http://tmorris.net/
> trait Graph[Node, Edge] <--- Looks great to meExcept you just killed Node and Edge as trait/class names. At the very
least, I'd call them ANode and AnEdge.
It is extremely important that type variable names contain no context (that is, call it what you want but I won't be projecting an imaginary context onto it). I am only motivated to point this out because one day someone is going to bring this point up IRL and I am going to have to deal with it. I don't want to; this elusive insight that I am apparently missing according to "the naming guys" is repeatedly shown to be a failed thesis and now it is crawling into the least welcome of places (type variables).
On Thu, Nov 17, 2011 at 7:56 AM, Matthew Pocock <turingate...@gmail.com> wrote:On 17 November 2011 12:11, Tony Morris <tonym...@gmail.com> wrote:>It is extremely important to pick meaningless names. The moment you
project meaning is the moment you start making all those mistakes that
we so often see.Many times the types are not devoid of meaning. Consider:trait Graph[A, B]
Bad. Now I have to remember whether your graphs have nodes first or edges first. I don't want to have to do that.
trait Graph[N, E]
Good! Now I don't have to remember!
trait Graph[Node, Edge]
Why all the extra letters? I already knew everything I needed to know.
Are you seriously expecting me to buy this? That I, and others who
aspire to get practical work done, are not "normal programmers", while
you, and your buddies are? Is this how you justify your thesis to
yourself? Sorry, but it's pretty transparent to me.
On Thursday, November 17, 2011 1:59:55 PM UTC-6, Tony Morris wrote:Are you seriously expecting me to buy this? That I, and others who
aspire to get practical work done, are not "normal programmers", while
you, and your buddies are? Is this how you justify your thesis to
yourself? Sorry, but it's pretty transparent to me.
Tony, you are not an the same galactic quadrant as a normal programmer. If I were a hiring manager evaluating you, I would put checks beside "brilliant", "highly productive", and some other related checkboxes, and then put in "reason for decline": "Not able to work effectively as part of a normal team."
Maybe I'm wrong. But everything I've seen about you on the net indicates both that you are probably worth about 10 normal programmers, and at the same time the code you produce would be completely unsupportable.
Ken
I find the single letter convention actually makes the use of generics
more obvious to me and easier to reason about. If I had to figure out
whether everything that looked like "Node" was a type parameter or a
concrete type I would be sad. [1]-- Erik
By the way, you have a huge selection bias. There really do exist people
who would also give a sensible answer to the question, and also endure
foolishness on behalf of the interviewer.
I suppose we should name our type variables then, because you are not
off the charts amirite?
> The story you told about being asked how to to reverse a list in an interview, and your response to the interviewer, only reinforce to me (and I suspect others) how completely off the charts you are
No, its the other way round. If you think that walking away after the interviewer rejects correct answeris inappropriate response, I'm pretty sure there are lots of people here happy to be off your charts.
PS: you might also like to know that your pseudo-psychological analysis,
likely influenced by pop culture while simultaneously having nothing to
do with reality, has real-life adverse impact on others. Just so you
know; something to think about, if you're into that. Thinking that is.
Try it.
I should note that I did, in fact, explain how the solution works, but
when you are up against people who have difficulty divorcing themselves
from the idea of making up stupid shit and resort to unintentional
bullying in order to give themselves the illusion of the legitimacy of
their position, you must make a prognostic assessment about the
effectiveness of continuing.
Many interviewers think they have some kind of upper-hand by virtue of
being the interviewer. They don't; it's an illusion. I certainly don't
want to be working with immutable idiocy or bullies.
Knowledge is not for everyone. Pick your battles.
Walking away due to poor prognosis of making sense is distinct to
walking away in disgust.
It's not always emotional dude, promise. Sometimes it's just what makes
a bit of fucking sense.
On Thursday, November 17, 2011 12:45:56 PM UTC-6, Rex Kerr wrote:On Thu, Nov 17, 2011 at 7:56 AM, Matthew Pocock <turingate...@gmail.com> wrote:On 17 November 2011 12:11, Tony Morris <tonym...@gmail.com> wrote:>It is extremely important to pick meaningless names. The moment you
project meaning is the moment you start making all those mistakes that
we so often see.Many times the types are not devoid of meaning. Consider:trait Graph[A, B]
Bad. Now I have to remember whether your graphs have nodes first or edges first. I don't want to have to do that.
trait Graph[N, E]
Good! Now I don't have to remember!
trait Graph[Node, Edge]
Why all the extra letters? I already knew everything I needed to know.
I disagree completely. To you, these extra characters may be unneeded but harmless; to another, they may be vital.
This is nothing more than that argument as to whether the loop counter should be n or loopCounter; IMHO, it should clearly be the latter (assuming you must do imperative programming.)
Why the freak are people so opposed to a few extra characters that don't even interfere with reading speed?
No, its the other way round. If you think that walking away after the interviewer rejects correct answeris inappropriate response
Am 17.11.2011 15:47, schrieb Matthew Pocock:
> Hi,
>
> Agreed. I have type parameters rendered in italic and type alases as
> italic+bold in intellij idea. I'm not a fan of Christmas-tree highlighting,
> but this seems to help.
+1 for Christmas-tree highlighting :) (let's abbr. it to CTH).
Maybe we should start a feature request for jline, for CTH in the REPL.
- --
Tschööö--->...Stefan
- ---------------------------
Don't visit my homepage at:
http://home.arcor-online.net/hirnstrom
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.10 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/
iEYEARECAAYFAk7F4PMACgkQQeATqGpDnRoELQCfezBzGfJJXzN6smxmSJM9ud0x
0OIAn3ztlqT82q4L9syY5Xh4n8lPn2j3
=2sEO
-----END PGP SIGNATURE-----
I don't want to put words in Tony's mouth but I will try to explain
what I think he is getting at.
Let's say you've written a class like so:
class DirectedGraph[Node, Edge](nodes:Set[Node], edges:Set[Edge]) {
...
def hasEdge(src:Node, dst:Node): Boolean = ...
}
(You could argue that you only need the edges in the constructor... it
doesn't really make a difference for this example.)
The point is, how do you implement hasEdge? Clearly we know that any
type representing an edge in a directed graph needs to store two nodes:
a source and a destination. But our type parameter allows us to pass
*any* type as "Edge". Graph[Int, Int] is a seemingly valid type (the
type system permits it and it would compile) but I don't see any
obvious way to use Int as an edge, especially with a totally
independent Node type parameter.
If you are stubborn and want to keep using type params but get things
working I think you'll find yourself using context bounds and doing
something like this:
trait Node
trait Edge[NodeType <: Node] {
val src:NodeType
val dst:NodeType
def equiv(src2:NodeType, dst2:NodeType) = src == src2 && dst == dst2
}
class Graph[NodeType <: Node, EdgeType <: Edge[NodeType]](nodes:Set[NodeType], edges:Set[EdgeType]) {
...
def hasEdge(src:NodeType, dst:NodeType): Boolean = {
edges.filter(_.equiv(src, dst)).isEmpty
}
}
...or something like this.
The point is that the whole example of a graph with type parameters for
nodes and edges is totally confused. Generic type parameters are
defined or explicated by the constraints put on them. "Node" and "Edge"
are useful descriptors for the constraints that make guarantees about
node/edge behavior (for example, the type which guarantees that edges
have a src node and dst node). I have a hard time imagining someone
seeing "NodeType <: Node" and finding it less confusing than something
like "T <: Node".
In the graph case, the type parameter doesn't really give you anything
over a normal OO graph example. If anything, a type parameter should be
used to give nodes a "payload" (e.g. a number, a string, and object)
that is stored in the graph. Here's a naive example of what I mean:
case class Node[T](payload:T)
case class Edge[T](src:Node[T], dst:Node[T])
case class Graph[T](nodes:Set[T], edges:Set[T]) {
def hasNode(node:Node[T]) = nodes.contains(node)
def hasEdge(src:Node[T], dst:Node[T]) = edges.contains(Edge(src, dst))
...
}
Since graphs are a data structure for storing data (like List) it makes
sense to me that you'd want to define Graph[T], not Graph[N, E]. I
think this is why Tony was asking you to provide the graph
implementation--because once you start it quickly seems incoherent.
Maybe you imagined something other than the examples I provided--I
can't imagine it making sense, but maybe I have missed the point
instead.
There are lots of examples of OO graph implementations as well as a
Scala implementation using inner types, so it should be easy to compare
those to an approach using something like Graph[Node, Edge].
Hope this was helpful. I'm certainly not saying either of my
implementations is the "only way to do it" but I think this is the
point that Tony and others were trying to make.
-- Erik
Am 17.11.2011 15:47, schrieb Matthew Pocock:
> Hi,
> Agreed. I have type parameters rendered in italic and type alases as
> italic+bold in intellij idea. I'm not a fan of Christmas-tree highlighting,
> but this seems to help.
+1 for Christmas-tree highlighting :) (let's abbr. it to CTH).
Maybe we should start a feature request for jline, for CTH in the REPL.
Guys, it seems The utility of this discussion has declined. Let's refrain from bloodying noses by continuing in the current vein.
I'll throw something out there.
Type parameters *are parameters * just like method parameters. If you think method parameters require a certain naming style, it is probably worthwhile to use this on type parameters.
If a type parameter looks like a class/type, that's similar to a method parameter looking like a member / term. Notice the adjustments to the unofficial style guide for this.
Guys, it seems The utility of this discussion has declined. Let's refrain from bloodying noses by continuing in the current vein.
I'll throw something out there.
Type parameters *are parameters * just like method parameters. If you think method parameters require a certain naming style, it is probably worthwhile to use this on type parameters.
If a type parameter looks like a class/type, that's similar to a method parameter looking like a member / term. Notice the adjustments to the unofficial style guide for this.
Am 17.11.2011 23:16, schrieb Matthew Pocock:
> You're self-evidently not a normal programmer. You care about types and
> correctness, but normal programmers care about writing the code and think
> about types afterwards, if at all.
Then I'm not a normal programmer too. You can't seriously think, that
paramters match for 'normal' programmers accidentally, do you?
> They become nodes or edges by virtue of their role in the graph. I'm not
> sure what other possible meaning there is to a node/edge in this context.
> It's a role they play, not something intrinsic to them. This role still
> needs naming, and the type parameter is the only place to name it.
That makes much more sense than to me, than the first paragraph.
Although - if Tony rejects such claims, my experience so far tells me,
that he does so with good reason.
Last year, I tried to solve the 99-scala-puzzles, which contained a
section of interesting tree-puzzles. As a *normal* programmer, who
didn't study informatics, I wasn't bored, because I hadn't much to do
with trees before, while programming. To solve the puzzles, I didn't
needed an Edge-class/-trait. My key element was a trait LR, which was
named LeftRight in the beginning, but I used it so often, that I rapidly
shortened the name to LR.
With l: Option[LR] and r: Option[LR] I defined the methods 'depth',
'preorder', 'inorder' and 'postorder', but while implementing, most of
the intermediate LRs vanished to nowhere. I didn't have an Edge, but a
case class Node and Leaf and object Node.
My abstract classs Tree extended LR, but used LR very rarely.
The complicated thing about it was the inheritance question; the
signature was:
abstract class TTree [+T <% Ordered[T]] extends LR { /...
and part of time I found myself in wild experimenting, how to make that
... compile. There are some T and U which could have been named PAYLOAD
or ELEMENT or E, but T seemed appropriate as well, and after T, there
comes U. Now I look at them, and they have no meaning, beside being the
content, which is put into that Tree.
LR is of course something like a Node, but I use it as a trait, not as a
type declaration. In the end, it is only the payload, which is encoded
by a Type-identifier (T, U) which has nearly no meaning.
It is like the Banana in the List of Bananas - the Banana doesn't help
in understanding the List.
- --
Tschööö--->...Stefan
- ---------------------------
Don't visit my homepage at:
http://home.arcor-online.net/hirnstrom
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.10 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/
iEYEARECAAYFAk7F8TkACgkQQeATqGpDnRrqnACgjvMYlRpct2YFB6wAn+DSR0xZ
OzAAn3t/kQL6I3aA6xLSOe0YF1gy3F3A
=T8wM
-----END PGP SIGNATURE-----
Type parameters have kinds. Method parameters have types. I don't see any value to your logic.
This is like saying if I accept a method of type Any, then its name is meaningless.
On Thu, Nov 17, 2011 at 11:22:38PM +0000, Matthew Pocock wrote:
> Show me the mistake I'm encouraged to make by thinking of graphs over nodes
> and edges modelled by the type parameters Node and Edge that I would not
> make naming these parametrized types A, B.
Let's say you've written a class like so:
class DirectedGraph[Node, Edge](nodes:Set[Node], edges:Set[Edge]) {
...
def hasEdge(src:Node, dst:Node): Boolean = ...
}
(You could argue that you only need the edges in the constructor... it
doesn't really make a difference for this example.)
The point is, how do you implement hasEdge?
Clearly we know that any
type representing an edge in a directed graph needs to store two nodes:
a source and a destination.
If you are stubborn and want to keep using type params but get things
working I think you'll find yourself using context bounds and doing
something like this:
trait Node
trait Edge[NodeType <: Node] {
val src:NodeType
val dst:NodeType
def equiv(src2:NodeType, dst2:NodeType) = src == src2 && dst == dst2
}
class Graph[NodeType <: Node, EdgeType <: Edge[NodeType]](nodes:Set[NodeType], edges:Set[EdgeType]) {
...
def hasEdge(src:NodeType, dst:NodeType): Boolean = {
edges.filter(_.equiv(src, dst)).isEmpty
}
}
...or something like this.
Hope this was helpful. I'm certainly not saying either of my
implementations is the "only way to do it" but I think this is the
point that Tony and others were trying to make.
-- Erik
That's odd - Map[A, B] extends A => B. Thinking about K's and V's might have blinded you to that fact
Since it's convention to have type names start with an upper case
letter, and variables (and function parameters) with a lower case
letter, and type parameters being one or two or maybe three upper case
letters, a quick glance at the above would lead me (at least) to think
that Node and Edge are types instead of type parameters. And since that
doesn't make sense in the context, there might be some initial cognitive
dissonance - at least there was for me. :)
I think
trait Graph[nodeType, edgeType]
is more readable.
Knut Arne Vedaa
I think
trait Graph[nodeType, edgeType]
is more readable.
Peace. Michael
I have to disagree, at least partially, here. Some software is highly
reusable just because they don't have a context...
I barely know Scalaz, mostly from what I have read here and there, but
my understanding is that it is very abstract, disconnected from specific
domains ("business logic"). Yet people find it very useful, because they
mastered the mechanisms it offers, and find them very useful when
applied to *their* business logic.
Somehow, it seems like a kind of meta-programming, offering tools to be
used in a more specific domain.
I see this kind of tool as highly reusable, yet without identity.
Somehow, it is similar for the collection library.
Of course, you can still name (semi-)abstract stuff, like "the thing we
iterate on", "the type of the cumulated result" or such.
Of course, in some domains, the names can draw on the underlying theory:
nodes and edges in graph theory, key and values in data maps, and so on.
But overall, specific names can get in the way.
Perhaps what is missing from the Scala doc is context. Somehow, it lists
together several methods but description is short, context can be
missing, etc.
I don't think the tradition of one letter type names will go away, but
in a few entries of ScalaDoc I looked at, the types are (succinctly)
explained in the Scala doc itself. So even if the names aren't very
explicit, the doc is here.
--
Philippe Lhoste
-- (near) Paris -- France
-- http://Phi.Lho.free.fr
-- -- -- -- -- -- -- -- -- -- -- -- -- --
Please show us how to quantify "... the min number of characters
[required] to uniquely represent [every function]."
And tell us how that relates to the concept of functional abstraction.
> Ken
Randall Schulz
The Map data structure is intrinsically associated with the concept of a data structure that represents some key instances drawn from a key set conforming to a Key type that 'map to' some value instances drawn from a value set/bag conforming to a Value type. If a person doesn't understand Key/Value (or K/V) because they don't know what a Map is, then they need to find out what a Map is. The issue you're raising in this case isn't with the naming of the type parameters, but that the entire concept modelled by the Map isn't known to the user. Once they understand the Map abstraction, it's much easer for a normal programmer to look at signatures with the type parameters Key/Value (or K/V) than A/B and associate this back to the Map abstraction. Normal people find names and mnemonics easier to remember than random letters. We can find any amount of published literature that demonstrates this experimentally, and probably also papers that specifically dissect out what sub-population this doesn't hold for (hint - this sub-population is not over-represented in that which is also your average programmer, certainly not to the point that you can discard using informative names in APIs aimed at average programmers).
I believe there are aleph_0 distinct discrete functions. Probably
aleph_1 distinct functions over continuous variables. Those are just
guesses, though...
> The observation was intended semiseriously...why represent _anything_
> with a number of characters greater than that necessary to guarantee
> a unique representation? Obviously, because further characters can
> add (for lack of a better term) meta-semantic meaning.
I did not detect the (implied) semi-humor. Sorry...
I don't know what "meta-semantic meaning" might be, but humans do
clearly benefit greatly from whatever connotative value they can derive
from the particular choice of symbols even though those symbols are, in
fact, entirely arbitrary.
> Ken
Randall Schulz
off-the-charts.
Type parameters are not just parameters like method parameters. The problem with projecting names onto type parameters, unlike method parameters, it that this departure from reality is *always* unwelcome (unlike coincidentally having no impact for method parameters). That is, you will always be wrong by virtue of them actually being universally quantified variables. You will never be right. There doesn't exist a case where projecting anything but "forall" onto a type parameter (in position of universal quantification) is sensible.
Guys, it seems The utility of this discussion has declined. Let's refrain from bloodying noses by continuing in the current vein.
I'll throw something out there.
Type parameters *are parameters * just like method parameters. If you think method parameters require a certain naming style, it is probably worthwhile to use this on type parameters.
If a type parameter looks like a class/type, that's similar to a method parameter looking like a member / term. Notice the adjustments to the unofficial style guide for this.
You crack me up mate.
You've got to pull your head out of your rear-end if you'd like to have a chat. You know, clue up, come to terms with the fact that "you're just crazy" is not an acceptable argument, while lecturing others on communication skills. By the way, that looks really dumb y'know? Having no clue on the subject matter, vomiting incoherent arguments, proclaiming your victory all the while thinking you are somehow qualified to lecture on communication skills.
Let me know when you are ready to work toward a coherent and aspirational goal, whatever it might be; the subject matter at hand or not. I'm serious; until then, you have nothing interesting to offer. Don't take it personally. I lament harder than you do, promise.
Just let me know when.
The funniest - and saddest - thing in all of this is that Tony is
trying very hard to dissuade you of the opinion that he is so
brilliant!
When I started with FP a few years ago I felt the same as you do now.
It seemed complex. Guys like Tony seemed uber smart and I thought I
could never learn as much as they had. But then I started learning
the basic concepts, and the concepts that build on those, and so on.
I realized just how wrong I was. Tony is a smart dude, maybe smarter
than I am maybe not. But he's certainly been around the FP block
longer than I, and most other people, have been. So he's learned -
really learned, not just glossed over the concepts like some people do
- about FP and the concepts involved. As I've seen him say in various
other threads like this, it's not like he was born with this
knowledge.
You are hurting yourself - and I wish it was only yourself, but more
on that in a second - by continuing to say, "Tony is so much smarter
than me and everybody else, we can't possible ever understand all this
stuff. It's too complex!" In fact, it's not complex at all. It only
looks that way to someone new to the concepts, much like calculus,
linear algebra and differential equations look complex to someone just
learning basic algebra. But these are things that _everyone_ can come
to understand given time and effort.
If you don't want to put in the time and effort to learn these things,
then fine. But stop raising everyone who has taken the time and put
in the effort up on a pedestal - yes, you are the one putting them
there, they are not putting themselves there - and using that as an
excuse for you not understanding! You do them a disservice by saying
that's it is some natural born gift that allows them to understand
these things and - to retract what I said earlier - you perpetuate the
myth to others, causing them to be scared away by these concepts!
(When I was learning about monads the biggest thing I had to overcome
was all the hype around them. I understood the basics pretty quickly,
but thought I was missing something because of all the blogs and
people talking about how hard monads are to understand. But they are
the simplest things possible!)
So please, please stop with this, "It's so complex and your so smart I
can't possibly hope to understand this stuff the way you do,"
nonsense.
That's not really fair, is it? I would say that the most
disappointing library has to be the Scala standard library. When I
started using Scala I couldn't make heads or tails of anything. I had
to read books and blogs to understand what was useful to accomplish
the tasks at hand. There were and still are several places in the
scaladoc where concepts - some only known in the Scala community - are
used in a brief sentence describing a method, but no further
explanation is given. This is pretty piss poor for something at the
very core of the language.
Now I'm sure you and everybody else are getting ready to hit the reply
button to tell me all about the great strides that have been made in
documenting the standard lib. And I agree, it has gotten much better
lately. But that didn't happen until Typesafe was founded and the
focus of the Scala creators and maintainers shifted from making Scala
useful to getting Scala used.
Scalaz's creators and maintainers are currently more focused on making
something useful rather than something that is widely used. There is
a ton of documentation in Scalaz - it's all in the types! - and there
is voluminous amounts of pdfs and blogs about the underlying concepts.
No, that documentation has not been *copied* into the source code.
That has not been a focus. It is expected that people interested
enough in learning will Google the concept or ask on the mailing list.
But if you, or anyone else, wants to improve the situation submit a
pull request. As long as descriptions are accurate, they'll be
accepted.
Rich
So well put Richard.
The amount of damage that is done by perpetuating the myth that understanding the subject matter at hand is somehow unapproachable by speaking to all these logical fallacies is just sad. No I really want born with categorytheory.so in my brain our whatever nonsense it may require to continue justifying these myths (they astound me every time, so please excuse my naive guessing).
Fine, don't understand it, fine call me names, call me crazy or lacking communication skills, use all the fallacies at your disposal. I DO NOT GIVE A FUCK THAT YOU ARE WRONG AND I REFUSE TO TAKE YOUR BULLYING PERSONALLY. But I do care that the next guy might fall for it. Thankfully Richard is not one of those and has taught me a few things on occasion and will likely continue to do so (thanks Richard).
To others, beware of the trap, especially the illusion of comfort it advertises. It's bullshit.
Post hoc ergo propter hoc.
Martin, I have no idea what you think I am wrong about, but I am pretty
sure you are wrong about what it is I do and I am also pretty sure you
need to look at category theory a bit more so that you can better
understand it. No, I don't do the "hard hard fringe of category theory."
I have no idea where you even got that idea. Did you just make it up?
The credentials are unpersuasive.
--
Tony Morris
http://tmorris.net/
I can be convinced of lots of things. I repeat, I was not born with my
current level of understanding and positions on various matters. I have
been convinced of various things to arrive where I am today. You've just
got to make sense in doing so. I am still not convinced that this is too
much to ask of the scala mailing list in general.
I was in the middle of a state forest on a dirt bike in outback
Australia when I responded to at least one of the messages. Time
management is not difficult.
I want to "quickly" comment on the whole Scalaz documentation/complexity thing.
Disclaimer: I say this as an (ab)user of Scalaz.
Programming is hard, learning totally new concepts is hard - the payoff has to be worth the investment. Documentation should provide enough of an overview to answer the "is it worth it" question.
Here Scalaz documentation is poor. It takes way too much time to be able to understand the why's and, perhaps more importantly the how's. The why's I will come back to, but the how's will be solved in large part by Scalaz 7.
7 makes a big shift in code organization, putting most type class instances right with their related type (not spread out over a few files). Scaladoc should then be a usable tool for discovering the how.
The why's on the other hand require a bit of searching - see the deriving presentations and read those scary Runar blog posts, hell you can even read about Elephants if it helps. I believe that the upcoming book might provide the overview that most need.
The important part is this: you will reinvent parts of Scalaz sooner or later. Key parts of your existing code base already do. Personally I have reinvented:
* Equal
* Validation (badly I might add)
* Iteratees
* Ephemeral stream
* Tree based zippers (although my implementation better fits my needs)
I was for a time also on the verge of reinventing arrows.
Any perceived complexity in Scalaz is due to how general it is and how hard it works around and within the type system to give you those goodies.
I write all this because these last few posts from Ken keep coming back to Scalaz, of course with some help from the ever communactive Tony.
It's a distraction - Scalaz is generally useful, and if you find yourself asking "this fp style thing seems it should be reusable" then look in Scalaz. If it's not obviously there ask in the Scalaz group, perhaps looking at it slightly differently makes all the difference.
To finish: I personally find it very comforting that, in Scalaz, there is a library that provides a one stop shop for solving many of my abstraction issues. I don't buy into "follow the types" you *do* need easier intros to get going (the internet is full of them) but the payoff *is* worth it - promise. (sorry Tony couldn't resist)
You're not the first person to accidentally reinvent many concepts in
scalaz. Even Martin did exactly that a couple of weeks ago.
I appreciate your honesty. It should probably go on the scalaz mailing
list though. No need to apologise.
Martin,
How do you propose we get people to knowingly discover concepts (in
contrast to unknowingly discover, as they currently do), when you have
seen the general attitude to learning these things? It's much tougher
than you might imagine -- indeed, look at the roundabout we went on
before you discovered that you'd invented "the essence of the iterator
pattern" in a previous thread. What about others?
You regularly ask me why I bother with this mailing list -- now I can
answer. It's because occasionally someone will contact me, in private or
something, who has had a genuine insight and is looking for discovery.
It's cheap for me when that happens because all I need to do is give the
appropriate leads and that person(s) goes off to discover for
themselves. Like I said, time management is not difficult and I will
pick off the easy fruit on this mailing list -- this includes putting up
strong resistance to the anti-think squad, which is important to others'
learning.
PS: You really need to let go of the category theory thing. It is
absolutely not representative of what we are discussing.