34 votes

F# vs C# vs Nemerle

J'ai un nouveau projet dans mon TODO et je ne peux pas choisir entre F# et Nemerle.

Je suis actuellement en train d'apprendre F# et j'ai quelques projets sur Nemerle.

J'aime la manière F#, j'aime l'indentation par défaut (je veux aussi l'indentation par défaut pour nemerle2), j'aime beaucoup de fonctionnalités et de magie de F# mais il n'y a pas de macros.

L'objectif de F# est VS2010 et peut-être (peut-être) une plus grande équipe de développeurs et il ressemble à Haskell (on peut créer des programmes Linux légers avec et c'est amusant).

Le but de Nemerle est les macros et je pense que j'aime plus certaines syntaxes de Nemerle.

et la plupart des gens aiment juste le C#...

  • La langue doit donc être une perspective
  • La syntaxe doit être logique, facile à comprendre, soignée et puissante (moins de code)
  • Il doit être facile à utiliser avec .NET
  • Donc je me suis déjà fermé avec le choix de ces 3 personnes.
  • Je peux les utiliser tous, mais qu'en est-il du choix d'un seul. Ce n'est pas si facile pour moi.

juste par exemple j'aime bien (Nemerle)

match(STT)
    | 1 with st= "Summ"
    | 2 with st= "AVG" =>
        $"$st : $(summbycol(counter,STT))"

beaucoup plus que (F#)

let (|Let|) v e = (v, e)

match stt with 
| Let "Summ" (st, 1) 
| Let "AVG" (st, 2) -> srintf "%s ..." st

F# :

["A"; "B"] |> List.iter (fun s  -> printfn "%d"  s.Length)

Nemerle :

["A", "B"].Iter(x => printf("%d", x.Length))

F# (j'espère ne pas me tromper ici) :

let type X =
    let mytable a = ""
    let mytable b = ""
    new(A, B) = {
    a <- A
    b <- B }
    member X.A 
        with get  = a
    member X.B 
        with get  = a

Nemerle :

[Record]
class X
  public A : string { get; }
  public B : string { get; }

C# :

class X
{
    private readonly string _a;
    public string A { get { return _a; } }

    private readonly string _b;
    public string B { get { return _b; } }

    public X(string a, string b)
    {
        _a = a;
        _b = b;
    }
}

et voici le code de Nemerle que je ne peux déjà pas convertir en F# (donc je ne fais que l'apprendre) ...

abstract class ABase
    abstract public A : string { get; }

interface IB
    B : string { get; }

[Record]
class My : ABase, IB
    public override A : string { get; }
    public virtual  B : string { get; }

Comparaison avec C# :

abstract class ABase
{
    abstract public string A { get; }
}

interface IB
{
    string B { get; }
}

class My : ABase, IB
{
    private readonly string _a;
    public override A : string { get { return _a; } }

    private readonly string _b;
    public virtual  B : string { get { return _b; } }

    public My(string a, string b)
    {
        _a = a;
        _b = b;
    }
}

Il est clair que le code Nemerle est plus facile à supporter et plus lisible.

@Brian C'est pourquoi je demande, montrez-moi si c'est réel pour rendre cela plus facile sur F#, C# aussi si vous voyez que je le fais mal parce que je ne suis pas sûr des autres façons de faire clairement la même chose.

15voto

Juliet Points 40758

montre-moi si c'est réel pour rendre cela plus facile sur F#, C# aussi si vous voyez que je le fais mal parce que je ne suis pas sûr des d'autres façons de faire clairement la même chose.

Vos exemples en F# et C# ne sont pas très concis. Réécrivons certains des exemples de votre OP :

Correspondance de motifs

Nemerle :

match(STT)
    | 1 with st= "Summ"
    | 2 with st= "AVG" =>
        $"$st : $(summbycol(counter,STT))"

F# :

Je ne suis pas sûr à 100% de ce que fait votre code, mais il semble qu'il soit basé sur cette réponse . Je ne pense pas qu'il soit facile de créer de nouvelles variables dans une expression de correspondance, mais je pense que les motifs actifs sont superflus.

J'écrirais le code comme ceci :

let st = match stt with 1 -> "Summ" | 2 -> "Avg"
sprintf "%s ..." st

Les cartes fonctionnent aussi :

let sttMap = [1, "Summ"; 2, "Avg"] |> Map.ofList
sprintf "%s ..." (sttMap.[stt])

I <3 La suggestion de Jon aussi :

let 1, st, _ | 2, _, st = stt, "Summ", "AVG"

Enregistrements

Nemerle :

[Record]
class X
  public A : string { get; }
  public B : string { get; }

F# :

type X = { A : string; B : string }

C# :

class X {
    public string A { get; private set; }
    public string B { get; private set; }

    public X(string a, string b) {
        A = a;
        B = b;
    }
}

Classes

Nemerle

abstract class ABase
    abstract public A : string { get; }

interface IB
    B : string { get; }

[Record]
class My : ABase, IB
    public override A : string { get; }
    public virtual  B : string { get; }

F# :

[<AbstractClass>]
type ABase() =
    abstract member A : string

type IB =
    abstract member B : string

type My(a, b) =
    inherit ABase()
    override this.A = a

    abstract member B : string
    default this.B = b
    interface IB with
        member this.B = this.B

Quelques éléments à noter ici :

  • Les interfaces F# sont définies à l'aide de l'élément abstract mot-clé. Vous pouvez les transformer en classes abstraites en utilisant le mot-clé [<AbstractClass>] attribut.

  • Les interfaces sont implémentées de manière explicite. En général, vous devez convertir un objet en une définition d'interface pour invoquer les membres de l'interface : let x = My("a", "b"); printf "%s" (x :> IB).B . Pour éviter le cast, vous devez créer des membres publics qui reflètent les méthodes de votre interface.

  • Les fonctions virtuelles définissent une abstract member avec un default mise en œuvre.

Vous mettez tous ces composants ensemble, et vous obtenez des définitions de classe qui sont nocives pour les nerfs oculaires. Mais ce n'est pas grave puisque les classes ne sont généralement pas utilisées très souvent. La plupart des modèles d'objets F# sont définis par des unions et des enregistrements, où les classes son utilisé, les hiérarchies de classes sont très plates au lieu d'être profondes, de sorte que vous ne voyez pas l'héritage ou les fonctions virtuelles utilisées aussi souvent en F# qu'en C#.

C# :

abstract class ABase {
    public abstract string A { get; }
}

interface IB {
    string B { get; }
}

class My : ABase, IB {
    public override string A { get; private set; }
    public virtual string B { get; private set; }

    public My(string a, string b) {
        A = a;
        B = b;
    }
}

Pour faire court, je pense que F# est assez comparable à Nemerle, mais on dirait que vous êtes en train de l'apprendre. Ne vous inquiétez pas, quand j'apprenais F#, j'écrivais du code laid et volumineux qui reflétait essentiellement le C# avec une syntaxe plus funky. Il a fallu un peu de temps avant que je puisse écrire de manière plus idiomatique.

Je recommande ce qui suit :

  • Si vous êtes familier avec Nemerle et aimez l'utiliser, continuez à le faire :)
  • Si vous voulez apprendre F#, je pense que votre projet est un bon endroit pour commencer. Je pense que vous pouvez écrire F# aussi proprement ou mieux que votre Nemerle.
  • Le C# est également acceptable, mais je ne le préférerais pas à l'un ou l'autre des langages si vous faites beaucoup de comparaison de motifs ou de traitement de symboles.

12voto

NN_ Points 809

Les versions F# et Nemerle sont très différentes :

  1. Nemerle définit les classes alors que F# définit les structures.
  2. Nemerle définit des membres en lecture seule alors que F# définit des membres mutables.
  3. Nemerle définit des propriétés publiques tandis que F# définit des champs publics.
  4. Nemerle définit le constructeur alors que F# ne le définit pas.

Le code analogue de Nemerle pour l'exemple F# est le suivant :

struct X
  mutable A : string
  mutable B : string

Le deuxième exemple est presque le même :

  1. Nemerle définit un constructeur pour My alors que F# ne le définit pas.
  2. Nemerle définit des propriétés qui retournent la valeur passée dans le constructeur alors que F# définit des propriétés avec des valeurs constantes.

La version Nemerle est beaucoup plus courte et plus claire que la version F# ici.

P.S. A propos de la syntaxe des accolades et de l'indentation. Nemerle supporte les deux syntaxes.

Vous pouvez écrire l'un ou l'autre :

class M
{
  static Main() : void
  {
    Console.WriteLine("A");
  }
}

Ou utilisez l'indentation :

#pragma indent
class M
  static Main() : void
      Console.WriteLine("A");

Ou même utiliser les deux styles !

#pragma indent
class M
  static Main() : void { Console.WriteLine("A"); }

4voto

Mark H Points 9127

La syntaxe d'un enregistrement F# est la suivante

type X = { 
    A : string
    B : string 
}

Quant au code que vous ne pouvez pas convertir

[<AbstractClass>]
type ABase() =
    abstract A : string

type IB = 
    abstract B : string

type MyClass(a, b) = 
    inherit ABase()
        override this.A = a
    interface IB with 
        member this.B = b

Je ne suis pas trop sûr de ce que votre exemple de filtrage essaie de faire, mais F# a la syntaxe suivante (je pense que vous la cherchez)

match (STT) with
    | 1 when st = "Sum" -> ...
    | 2 when st = "Avg" -> ...

3voto

Lazin Points 4481

F# a une meilleure syntaxe d'enregistrement :

type X = struct
  val mutable A : string
  val mutable B : string
end

et voilà votre code, converti en F# :

[<AbstractClass>] 
type ABase = 
    abstract member A : string
    // you can declare default implementation using default keyword like this:
    // default this.A with get () = "default implementation"

type IB =
    interface
        abstract member B : string
    end

type My = class
    inherit ABase
        override my.A with get() = "A"
    interface IB with
        member my.B with get() = "B"
end

2voto

Onorio Catenacci Points 6130

"Il est clair que le code Nemerle est plus facile à supporter et plus lisible."

Plus facile à soutenir pour qui ?

Plus lisible pour qui ?

Sur quoi fondez-vous ce jugement si ce n'est sur votre propre expérience de la langue et vos préjugés personnels ? J'ai codé en C++ pendant des années et je trouve donc tout langage qui n'utilise pas d'accolades pour délimiter les blocs de code un peu contre-intuitif. Mais d'autres personnes qui ont codé en Lisp (ou dans des langages basés sur Lisp) trouveraient probablement l'idée d'utiliser des accolades très étrange et donc contre-intuitive. Les personnes qui ont codé en Pascal ou dans des langages basés sur le Pascal préféreraient voir "begin" et "end" pour délimiter les blocs de code - ils trouveraient cela "plus facile à supporter et plus lisible".

Avez-vous des études qui prouvent que la compréhension du code est plus élevée avec la syntaxe Nemerle qu'avec F# ou C# ? Parce que ce serait la seule mesure empirique et objective à laquelle je pense qui prouverait que le code Nemerle est "plus facile à supporter et plus lisible".

Pour le développeur professionnel, il s'agit simplement de trois syntaxes différentes. À moins que vous ne compariez un langage à Brainf*ck En fait, il s'agit simplement de savoir à quel type de syntaxe vous êtes déjà habitué et combien de temps il vous faut pour apprendre une nouvelle structure syntaxique.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X