Documentation for class properties is hard to understand

135 views
Skip to first unread message

Chris Burt-Brown

unread,
Nov 28, 2015, 7:38:42 PM11/28/15
to Haxe
Enjoying haxe so far, but want to report a snag. I've had to give up using class properties. The language might be fine, but the documentation is incomprehensible to me. I am looking at haxe.org/manual/class-field-property.html and the subsequent pages. Is this the first page for explaning properties? Because if so, I don't think it's adequate at all.

What I gather from that page is that I need a var property and appropriate functions to map to getters and setters. The docs completely skip over all of this, and jump straight to access specifiers. The first code sample is enigmatic, and I'm not sure if it's even complete, is it valid? It doesn't have the get_ and set_ functions? Is that supposed to be allowed?

There are code samples later which hint at what the correct syntax is, but even then they don't actually explain what they are doing. I've tried to replicate them as they're written, but I got as far as this:

public var tool(get, set):ITool;
function get_tool() return _currentTool;
function set_tool(t) return t;

And by my testing, the setter is inert, it doesn't work, it's not setting the backing variable. I don't know why, but I'm just going to drop back to standard getX/setX functions, and leave it. Perhaps there is a bug in the way it compiles to Flash, or maybe my code is wrong, but I can't tell because I don't understand what the syntax is supposed to be.

I'm sure if I search these forums hard enough, I will find the answer to my problem, but a few minutes browsing didn't get me it, and I want the larger issue to be reported -- I don't think the current state of docs on Class Properties are anywhere near clear enough.

Dan Korostelev

unread,
Nov 28, 2015, 9:26:55 PM11/28/15
to Haxe
Your set_tool function doesn't indeed do anything, as it's body is simple "return t", so why would you expect it to change your storage variable? Maybe you meant something like "return _currentTool = t".

Also, the first example from the doc is defining a property that doesn't require getter and setter. If you read a bit further, it describes what `default` and `null` access specifiers mean. If you compare that to C#, (default,null) is like { get; private set; } auto-property.

воскресенье, 29 ноября 2015 г., 3:38:42 UTC+3 пользователь Chris Burt-Brown написал:

Chris Burt-Brown

unread,
Nov 28, 2015, 9:52:03 PM11/28/15
to Haxe
Hi there Dan.

You tell me that I should be writing "return _currentTool = t", but I ask, where do the docs say this? I cannot find any example anywhere in the docs which uses the form you give. In the section "Common accessor identifier combinations" there is an example which was using the syntax I had, but it doesn't explain why it works or what it does, so I have to guess at the correct syntax.

You tell me that the "default" keyword is explained in the docs. Where is this? I have read the docs again and I can't find any place where it explains what the "default" keyword does. It seems to be missing. Could you quote the part?

What you are telling me about haxe makes perfect sense, however, you are telling me basic information that seems to be missing from the documentation, unless I am looking in the wrong place.

chris

Chris Burt-Brown

unread,
Nov 28, 2015, 10:04:11 PM11/28/15
to Haxe
Are you referring to this?

default: Allows normal field access if the field has public visibility, otherwise equal to null access.
null: Allows access only from within the defining class.

Because I do not understand it at all. I don't think it makes any sense as written. What is "Allows normal field access"? What is the "normal field"? It should be possible to write this without so much confusion.

Nico May

unread,
Nov 28, 2015, 10:09:38 PM11/28/15
to Haxe
"Normal field access" - means it acts the same as a standard variable would, so if its set to public, its visible to other classes, if not, its not.

Nico

--
To post to this group haxe...@googlegroups.com
http://groups.google.com/group/haxelang?hl=en
---
You received this message because you are subscribed to the Google Groups "Haxe" group.
For more options, visit https://groups.google.com/d/optout.

Chris Burt-Brown

unread,
Nov 28, 2015, 10:18:26 PM11/28/15
to Haxe
Ok, that makes sense now, but I think I am right that the documentation is unclear. "Normal field access" doesn't explain that there will be an automatically generated variable behind the scenes. I wouldn't have guessed it meant that.

Nico May

unread,
Nov 28, 2015, 10:23:51 PM11/28/15
to Haxe
hang on, it doesn't mean that, it just means that its the same behaviour for access, public or private

Nico May

unread,
Nov 28, 2015, 10:24:37 PM11/28/15
to Haxe
I think you can use @:isVar (something like that) to auto-generate the variable, but thats not what it means

Chris Burt-Brown

unread,
Nov 28, 2015, 10:29:23 PM11/28/15
to Haxe
Now I'm confused again. The very first example shows just

  public var x(default, null):Int;

and nothing else. So if there is no variable behind, and no get function, then isn't this invalid code, or how can it work?

Nico May

unread,
Nov 28, 2015, 10:32:39 PM11/28/15
to haxe...@googlegroups.com
oh yes that works sorry, I was confusing when you use (get_x, set_x) where no variable is created automatically (as far as I know)

Chris Burt-Brown

unread,
Nov 28, 2015, 10:40:14 PM11/28/15
to Haxe
I'm afraid I'm still quite confused on all counts ... I'm not sure what any of these keywords are meaning anymore. Maybe it's just too late in the evening, but I think there must be a simpler way to describe it all ...

I don't think the documentation ever says if there will be a backing variable. It definitely should because without that information, it's really hard to understand what the code is doing. But in general the whole section is very unclear. I think it needs rewriting from scratch.

Chris Burt-Brown

unread,
Nov 28, 2015, 10:47:46 PM11/28/15
to Haxe
In most language like C#, a "property" is not a variable, it is just a pair of methods made to look like a variable.

But it is starting to look like in Haxe a property is completely different. A property is actually a real variable, but it just has some extra flags on it, giving you the ability to override the "read" and "write" functionality. If that's the case it's unlike any notion of "property" I've ever used before which is part of the source of my confusion.

Nico May

unread,
Nov 28, 2015, 10:54:30 PM11/28/15
to haxe...@googlegroups.com
I think c# has similar properties in functionality if I remember correctly, there is (as in haxe) the form where its simply 2 methods that act like a variable, but there is also a quick form, so you can for instance block set access on a variable (private set, I think it was mentioned already) in this case there is a variable generated

On Sat, Nov 28, 2015 at 7:47 PM Chris Burt-Brown <broke...@gmail.com> wrote:
In most language like C#, a "property" is not a variable, it is just a pair of methods made to look like a variable.

But it is starting to look like in Haxe a property is completely different. A property is actually a real variable, but it just has some extra flags on it, giving you the ability to override the "read" and "write" functionality. If that's the case it's unlike any notion of "property" I've ever used before which is part of the source of my confusion.

--

Simon Krajewski

unread,
Nov 29, 2015, 5:11:49 AM11/29/15
to haxe...@googlegroups.com
Hi,

I don't want to dismiss your criticism, but I think a lot of your confusion comes from tring to map C#'s property system to Haxe's, which doesn't quite fit. You're assuming that all properties operate on getter and setter methods, but that's not the general case for Haxe properties.

Something I'll concede is that the first setter example at http://haxe.org/manual/class-field-property-common-combinations.html doesn't actually show a proper setter because it does not set anything. We could change that slightly, but I would like to avoid introducing @:isVar at this point.

As for the "normal field access", I'm not sure how else to phrase that. I suppose we could reference the section on variables there?

I'm also not opposed to a section along the lines of "Differences to properties in other languages" which mentions some of the key differences between e.g. C# and Haxe properties. This would have to be authored by someone more familiar than me regarding the languages in question.

Let me know if you have any other concrete ideas regarding documentation.

Simon

Chris Burt-Brown

unread,
Nov 29, 2015, 7:46:03 AM11/29/15
to Haxe
I am starting to understand what the problem is. It's the *default* keyword. It's so hard to understand what this does, with the documentation as it is written. If you skip ahead to page 4.2.3 for a second, it explains that *default* and *null* make the property "physical". What "physical" means is that the property contains an implicit variable. This is actually the most important thing to know, but it's at the end. When you understand that, the very first code sample 

    class Main {
      public var x(default, null):Int;
      static public function main() { }
    }

now makes sense, because you can see that "default" keyword makes a variable exist behind the property.

But 4.2 is missing this information. What it says instead is this:

    default: Allows normal field access if the field has public visibility, otherwise equal to null access.

This is very confusing because of the terms used:

    - "Access modifier" = public, protected, private
    - "Access identifier" = default, null, get/set, dynamic, never
    - "Normal field access" = generate backing variable = "physical"

All three are separate and unrelated. Do you see why I was struggling to understand? The word "access" is so overloaded by this point that "normal field access", what is that? I actually initially thought that "normal" maybe meant "public"? And this lead to horrible confusion.

I am even more confused by the stuff in 4.2.1. It gives a few examples of how to use the property functionality, but the one that I was after was the example of a getter & setter. There actually is an example of this, but it reads:

  public var x(get, set):Int;
  function get_x() return 1;
  function set_x(x) return x;

What?? What does THAT mean? Why does the setter only contain a "return" statement and nothing else? Why are the braces missing? What does any of this code do? It should be explained. I still don't understand what the intent of this code sample is.

You can see in my first post that I am completely confused about properties by this point.

Philippe Elsass

unread,
Nov 29, 2015, 8:49:35 AM11/29/15
to Haxe

This page of the documentation should describe the specification, but also provide examples for all the combinations. This is rather important I agree.

Jonas Malaco Filho

unread,
Nov 29, 2015, 11:45:29 AM11/29/15
to Haxe
I too think that much of your confusion comes from trying to map another language's property system to Haxe's.

It's a natural thing to do so, but it's making you over think things: a variable is just a variable, and "normal" is variable-like.

See if the following helps:

A class field is a variable, property or method of a class which can either be static or non-static.
  • Variable: A variable class field holds a value of a certain type, which can be read or written.
  • Property: A property class field defines a custom access behavior for something that, outside the class, looks like a variable field.
  • Method: A method is a function which can be called to execute code.
Variable fields hold values, a characteristic which they share with most (but not all) properties:
 
class Main {
static var member:String = "bar";
[...]
 
Next to variables, properties are the second option for dealing with data on a class. Unlike variables however, they offer more control of which kind of field access should be allowed and how it should be generated.

When dealing with properties, it is important to understand the two kinds of access:

 

A read access to a field occurs when a right-hand side field access expression is used. This includes calls in the form of obj.field(), where field is accessed to be read.

A write access to a field occurs when a field access expression is assigned a value in the form of obj.field = value. It may also occur in combination with read access for special assignment operators such as += in expressions like obj.field += value.

 

Read access and write access are directly reflected in the syntax, as the following example shows:

 
class Main {
public var x(default, null):Int;
[...]

For the most part, the syntax is similar to variable syntax, and the same rules indeed apply.

The access identifiers define the behavior when the field is read (first identifier) and written (second identifier). The accepted values are:

  • default: Allows normal (as in variable-like) field access if the field has public visibility, otherwise equal to null access.
  • null: Allows access only from within the defining class.
  • get/set: Access is generated as a call to an accessor method. The compiler ensures that the accessor is available.
  • dynamic: Like get/set access, but does not verify the existence of the accessor field.
  • never: Allows no access at all.
An accessor method (or short accessor) for a field named field of type T is a getter named get_field of type Void->T or a setter named set_field of type T->T.
 
Both getter and setter may access their physical field for data storage. The compiler ensures that this kind of field access does not go through the accessor method if made from within the accessor method itself, thus avoiding infinite recursion.

However, the compiler assumes that a physical field exists only if at least one of the access identifiers is default or null.

(Thus,) A field is considered to be physical if it is either

  • a variable
  • a property with the read-access or write-access identifier being default or null
  • a property with :isVar metadata
(the above were excerpts of the manual, in the original ordering, with only a couple of annotations)

******

As to "Why does the setter only contain a "return" statement and nothing else? Why are the braces missing?"

First, the setter is an otherwise normal method, and as such, has a body that must be an expression.  In particular, it can be a single expression (like return x) or a block of expressions ({ trace(x); return x; }).

Then, there's no requirement that the setter actually use the value, although it still needs to return something to respect the T->T signature.
 

Chris Burt-Brown

unread,
Nov 29, 2015, 12:15:38 PM11/29/15
to Haxe
Hi Jonas,

I should point out that in this point in the process, having talked to everyone else in this topic and working through my confusion, I now believe myself to have a correct understanding of Haxe properties. I read through everything you posted and it does make sense now.

However, I still don't believe that the documentation presents this very well. I was never under the false belief that properties were "like" the properties in, say, C# (which I have indeed used before). It is obvious from the first code sample is that the reader should expect something quite different (which I understood and I don't have a problem with this).

But you can see from my conversations with people that I had several false models of how properties work. The code I posted when starting this topic was indeed gibberish, I admit that, but it was constructed from the sample in 4.2.1, which I now understand to be deliberately broken. The setter that was given was

function set_x(x) return x;

I interpret this as "oh, so there's some magic behind the scenes, and the value x returned will now be assigned to the property's internal variable". It turns out that no, there is no such magic, it is just a setter which does nothing. This is definitely a mistake in the documentation. I understand why the setter is of type T->T, that makes sense, but I would recommend that the setter should not be a no-op unless this is clearly labelled. In comparison, the documentation in C# uses the example of a setter that converts units, which is arbitrary but much more understandable and straightforward, and helps to explain clearly how setters work.

Regarding the missing braces, these only serve to further increase the suspicion that these are not normal functions, that they are doing some unknown magic. I did look at the section on Methods but couldn't find anywhere that said explicitly that the {braces} are optional. It turns out that actually set_x is actually just a normal function written in a non-standard way. I would recommend making this code more standardized since it has nothing to do with method syntax and omitting the braces increases confusion.

Andrzej Smolinski

unread,
Dec 4, 2015, 10:51:24 AM12/4/15
to Haxe
Just play with it in sandbox: http://try.haxe.org/#95A21

Look at http://stackoverflow.com/questions/27931070/haxe-property-getters-and-setters-versus-isvar


MY CONSTRUCTIVE THOUGHTS
<NO OFFENCE>

IMHO It's a REAL PAIN with Haxe docs:
- you must to dig for everything;
- lack of usable code examples/annotations;
- lack of "User Contributed Notes" like in php (with voting) or better StackOverflow;

Docs are almost dead:
- it isn't really community driven;
- it doesn't evolve because it's hard to maintain (tex, not md) and contribute;
- doesn't get improvements from community notes/comments etc.
- maintainers sees it as enought;
- as in the thread - assumes former knowledge - giving a STEEP LEARNING CURVE;
- doesn't contain links to valuable knowledge - f.e. http://blog.onthewings.net/2012/10/14/haxe-tips-everything-is-an-expression/


The same with HAXELIB:
- there are statistics but not time-aware (popularity) - you can be driven to depreciated/dropped;
- on other side there is abunch of great libs you should be aware of (are used daily by big teams) but you won't filter them out on the site (you'll find their names on blogs, in articles, on twitter or on conferences)
- lack of "star" or other voting mechanism;
- lack of public usage stats (user/firm profil list) - "In my stack";


All the Haxe KNOWLEDGE is VERY FRAGMENTED

https://matthijskamstra.github.io/haxejs/follow.html - try to collect this knowledge on your own

BUT there is a light in tunnel: http://haxe.io - simply has the most important "submit a link" and "correct" by github

http://matthijskamstra.github.io/haxenode/haxenode/why.html - shouldn't be linked in docs?


There is a discussion about haxe taking its place at every WWX - it won't with these docs. It doesn't evangelize itself. Give right tools to community - they will take care.
</NO OFFENCE>


Atul Kumar

unread,
Dec 4, 2015, 11:25:24 AM12/4/15
to Haxe
+1

I agree. I also got the same feeling when I got started.

I am not sure how much the community will take care of documentation when there are tools to easily contribute. Writing docs seems very cumbersome for most people (including me).

But still I think it would be great to implement those things you suggested. There is at least potential for a big improvement in that regard.

Simon Krajewski

unread,
Dec 4, 2015, 11:26:42 AM12/4/15
to haxe...@googlegroups.com
Am 04.12.2015 um 16:51 schrieb Andrzej Smolinski:
> Just play with it in sandbox: http://try.haxe.org/#95A21
>
> Look at
> http://stackoverflow.com/questions/27931070/haxe-property-getters-and-setters-versus-isvar
>
>
> MY CONSTRUCTIVE THOUGHTS
> <NO OFFENCE>
>
> IMHO It's a REAL PAIN with Haxe docs:
> - you must to dig for everything;
> - lack of usable code examples/annotations;

I agree we can have more examples. There's actually an open issue about
it [1].

> - lack of "User Contributed Notes" like in php (with voting) or better
> StackOverflow;

This was discussed before and dismissed. We actually had something like
that on the old API docs and it never took off in any notable way. I
realize many people praise the PHP documentation for it, but that is
largely because PHP's API documentation itself is garbage.

>
> Docs are almost dead:
> - it isn't really community driven;
> - it doesn't evolve because it's hard to maintain (tex, not md) and
> contribute;
> - doesn't get improvements from community notes/comments etc.
> - maintainers sees it as enought;

Oh please, the HaxeManual repository has 122 closed pull requests [2]
and almost 40 different people appear in the commit list [3]. There are
plans to move the sources to markdown [4] and once that is done we will
see if the contribution amount really increases.

> - as in the thread - assumes former knowledge - giving a STEEP
> LEARNING CURVE;

That's not uncommon for technical documentation. We cannot repeat all
the relevant information in every section, so the best we can do is
back-reference it properly.

> - doesn't contain links to valuable knowledge - f.e.
> http://blog.onthewings.net/2012/10/14/haxe-tips-everything-is-an-expression/

I don't mind adding some links to informative articles. Maybe a "Further
reading" box would be good for that.

If you want to help, open issues with _concrete_ suggestions of what to
do. I can't work with "We should do something somewhere/everwhere", you
have to point out specific sections and what you would like to see done.
Even better yet, send pull requests! I realize you're put off by the
sources being .tex, but adding a code example is a matter of adding a
.hx file to the the assets directory [5] and then put
`\haxe{assets/FileName.hx}` where you want it.

Thanks!
Simon

[1] https://github.com/HaxeFoundation/HaxeManual/issues/170
[2] https://github.com/HaxeFoundation/HaxeManual/pulls?q=is%3Apr+is%3Aclosed
[3] https://github.com/HaxeFoundation/HaxeManual/graphs/contributors
[4] https://github.com/HaxeFoundation/HaxeManual/issues/194
[5]
https://github.com/HaxeFoundation/HaxeManual/tree/master/HaxeManual/assets

Atul Kumar

unread,
Dec 4, 2015, 11:43:08 AM12/4/15
to Haxe
Then on the other hand, when I compare the state of documentation with for example Openframeworks or Cinder Haxe does actually pretty well!


On Friday, December 4, 2015 at 4:51:24 PM UTC+1, Andrzej Smolinski wrote:

Rafael Oliveira

unread,
Dec 4, 2015, 12:09:12 PM12/4/15
to Haxe
I think this project is a good start to put all the community docs in one place:


If I remember this will not be the final url, but once done it can be a link in the haxe website

Andreas Mokros

unread,
Dec 4, 2015, 12:53:47 PM12/4/15
to haxe...@googlegroups.com
Hi.

On Fri, 4 Dec 2015 17:26:37 +0100
Simon Krajewski <si...@haxe.org> wrote:
> realize many people praise the PHP documentation for it, but that is
> largely because PHP's API documentation itself is garbage.

Well, not complete garbage maybe, but if you dig a bit deeper in the PHP docs,
you see many functions without any examples, that are sparsely (or wrongly)
documented (or not at all) and many comments that aren't useful at all or like
ten years old and completely outdated, so I also wouldn't take the PHP docs as
a real good example.

> > or better StackOverflow;

Yes, maybe much more questions should be asked (and answered of course) at
SO. I have the feeling that many useful answers get lost here.

> There are plans to move the sources to markdown [4]

I think that's a good idea. Has there been any progress since September? Maybe
that would be worth a paid job?

> adding a code example is a matter of adding a
> .hx file to the the assets directory [5] and then put
> `\haxe{assets/FileName.hx}` where you want it.

Good to know. That hasn't documented somewhere, has it?

--
Mockey

Andrzej Smolinski

unread,
Dec 4, 2015, 8:37:12 PM12/4/15
to Haxe

> - lack of usable code examples/annotations;

I agree we can have more examples. There's actually an open issue about
it [1].

This can be done with simple list of user submitted urls (only try.haxe.org  and direct github resources/files)
"Related code snippets" - doesn't need to premoderate
Just rows with [title, date, link, stars/votes] - ideally with comments
Try.org sources could be simply listed in popups (or acordions) beside links to exploring how it works.

BTW [1] 1-st link shows nice github feature - LINE COMMENTS and that discussion/commited code is in some way important too - I guess it as equal to whole Andy's article - as PRACTICAL USE CASE. IMHO it's worth PINning (pinterest) to 'related code list'.


> - lack of "User Contributed Notes" like in php (with voting) or better
> StackOverflow;

This was discussed before and dismissed. We actually had something like
that on the old API docs and it never took off in any notable way. I
realize many people praise the PHP documentation for it, but that is
largely because PHP's API documentation itself is garbage.
 
In this case SO is a garbage too - this is almost the same idea.

For saving 'PURITY OF MANUAL' comments aren't part of "book" like margins annotations CAN, not MUST BE printed on 'paper version'.

LET THEM LIVE, let people upvote or deny, comment it, discuss, let others "cherry pick" what they search/needs - let yourself (and other moderators) to pick cherries from comments into "managed parts" of manual.

> Docs are almost dead:
> - it isn't really community driven;
> - it doesn't evolve because it's hard to maintain (tex, not md) and
> contribute;
> - doesn't get improvements from community notes/comments etc.
> - maintainers sees it as enought;

Oh please, the HaxeManual repository has 122 closed pull requests [2]
and almost 40 different people appear in the commit list [3]. There are
plans to move the sources to markdown [4] and once that is done we will
see if the contribution amount really increases.
 
This can help, but many of us (including me) doesn't have enought experience, knowledge or expressivity to write haxe manuals DIRECTLY. [I wrote many ActionScript FAQs/tutorials]

[4] IMHO THE GREATEST value will be with Inline Comments (for questions/explanations) and Discussions (for tips/code snippets/best practices) - not for writing, but for sharing/collab/community/social things.

> - as in the thread - assumes former knowledge - giving a STEEP
> LEARNING CURVE;

That's not uncommon for technical documentation. We cannot repeat all
the relevant information in every section, so the best we can do is
back-reference it properly.
 
Thats right BUT with TOO MANY places I read manual AS HOW HAXE COMPILER WORKS [explanations] - not AS HOW TO USE HAXE (language features) IN PRACTICE. It shows how it works but not how to use it to work as expected.

Look at http://haxe.org/manual/class-field-property-common-combinations.html not as compiler aware geek.
Compare it with http://try.haxe.org/#95A21 - IMHO it faster and clearer EXPLAINS WHY sth returns sth ('unexpected') and HOW to use/modify it for other cases.

> - doesn't contain links to valuable knowledge - f.e.
> http://blog.onthewings.net/2012/10/14/haxe-tips-everything-is-an-expression/

I don't mind adding some links to informative articles. Maybe a "Further
reading" box would be good for that.
 
+1
Just like "Related code snippets" list.(title, date, url, star/vote) - JUST another SITE COMPONENT - not direct, 'SERIOUS' manual content.

If you want to help, open issues with _concrete_ suggestions of what to
do. I can't work with "We should do something somewhere/everwhere", you
have to point out specific sections and what you would like to see done.

HMMM, maybe "give a 5" [min] to http://flarum.org/features/ ? [I used newsgroups for years but a vBulletin forum was the right base/tool for bigger [>15K users] community]. Moderating/FAQs/sticking/annoucements ... Flarum looks nice and modern (laravel/mithrill).
I'd like to read/ask/discuss about libs/use cases/performance/advices ... ufront/haxeui/openfl/nme/nodejs/flixel/flambe/snow/lime/kha ... whatever - Should I go to SO? "NOT A REAL QUESTION, closing". Site "use cases" looks outdated .... there is too big domain knowledge/usage possibilities to "manage linearly".
 
Even better yet, send pull requests! I realize you're put off by the
sources being .tex, but adding a code example is a matter of adding a
.hx file to the the assets directory [5] and then put
`\haxe{assets/FileName.hx}` where you want it.
 
as above - .tex isn't a great problem -
I'm not feeling ready to contribute manuals directly.
I don't have a place to EASY submit some code/comment to improve it's readability for newcomers (on medium.com I can comment/highlight out any text selection) or share sth related (and IMHO is important) I found somewhere.
I almost forget about manuals as a knowledge source - exploring it vary rarely, google often directs me to old api or other sites...


IDEA: Try.org resources could be searchable on tags basis.
I could assign "class", "property", "setter", "access", "scope", "field" for my submits/explorings.
The same could be done for manual content - for automatic matching/suggesting related code snippets.
Next SITE COMPONENT ("meteor/reactive way").


Reply all
Reply to author
Forward
0 new messages