c# .net f# nemerle

F#vs C#vs Nemerle



.net (8)

"Claramente, el código de Nemerle es más fácil de admitir y más legible".

Más fácil de apoyar a quién?

Más legible para quién?

¿Sobre qué estás basando ese juicio además de tu propia experiencia del lenguaje y tus propios prejuicios personales? He codificado en C ++ durante años y, por lo tanto, encuentro que cualquier lenguaje que no utilice llaves para delimitar bloques de código es un poco contrario a la intuición. Pero otras personas que han codificado en Lisp (o lenguajes basados ​​en Lisp) probablemente encontrarían la noción de usar llaves (llaves) muy extraña y por lo tanto contra-intuitiva. Las personas que codificaron en Pascal o en idiomas basados ​​en Pascal preferirían ver "comenzar" y "finalizar" para delimitar los bloques de código: encontrarían que "es más fácil de admitir y más legible".

¿Tiene algún estudio que demuestre que la comprensión del código es mayor con la sintaxis de Nemerle que con F # o C #? Porque esa sería la única medida empírica y objetiva que puedo pensar que probaría que el código de Nemerle es "más fácil de apoyar y más legible".

Para el desarrollador profesional, son solo tres sintaxis diferentes. A menos que esté comparando un lenguaje con Brainf * ck, básicamente es solo una cuestión de a qué tipo de sintaxis ya está acostumbrado y cuánto tiempo debe aprender una nueva estructura sintáctica.

Tengo un nuevo proyecto en mi TODO y no puedo elegir F # o Nemerle.

Actualmente estoy aprendiendo F # y tengo algunos proyectos en Nemerle.

Me gusta F # manera, me gusta la sangría por defecto (también quiero sangría por defecto para nemerle2), me gustan muchas características y magia de F # pero no hay macros.

El objetivo de F # es VS2010 y tal vez (quizás) el equipo de desarrolladores más grande y se parece a Haskell (puede crear programas ligeros de Linux con él y es divertido).

El objetivo de Nemerle es macros y creo que me gusta más la sintaxis de Nemerle.

y a la mayoría de la gente le gusta C # ...

  • Entonces el lenguaje debe ser perspectiva
  • La sintaxis debe ser lógica, fácil de entender, ordenada y potente (menos código)
  • Debe ser fácil de usar con .NET
  • Así que ya cerré mi yo con el elegido de estos 3 ones.
  • Puedo usarlos todos, pero ¿qué hay de elegir uno? No es tan fácil para mí.

solo por ejemplo me gusta (Nemerle)

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

mucho más 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 # (espero que no se equivoque aquí):

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; } }

y aquí está el código de Nemerle que ya no puedo convertir a F # (así que solo lo estoy aprendiendo) ...

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; }

Comparación con 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; } }

Claramente, el código de Nemerle es más fácil de admitir y más legible.

@Brian Así que es por eso que pregunto, muéstrame si es real para hacer esto fácil en F #, C # también si ves que lo hago mal porque no estoy seguro de otras formas de hacer claramente lo mismo.


F # tiene una mejor sintaxis de registro:

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

y allí está tu código, convertido a 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


La sintaxis para un registro F # es

type X = { A : string B : string }

En cuanto al código, no puedes 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

No estoy muy seguro de qué intenta hacer tu ejemplo de coincidencia de patrones, pero F # tiene la sintaxis (creo que estás buscando)

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


Las versiones de F # y Nemerle son bastante diferentes:

  1. Nemerle define clase mientras que F # define struct.
  2. Nemerle define miembros readonly mientras que F # define miembros mutables.
  3. Nemerle define las propiedades públicas mientras que F # define los campos públicos.
  4. Nemerle define constructor mientras que F # no lo define.

El código de Nemerle análogo para el ejemplo de F # es el siguiente:

struct X mutable A : string mutable B : string

El segundo ejemplo es casi el mismo:

  1. Nemerle define constructor para My while F # no lo define.
  2. Nemerle define Propiedades que devuelven el valor pasado en el constructor mientras que F # define propiedades con valores constantes.

La versión de Nemerle es mucho más corta y más clara que la versión F # aquí.

PS Acerca de las llaves versus la sintaxis de sangría. Nemerle admite ambas sintaxis.

Puedes escribir:

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

O use sangría:

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

¡O incluso usa ambos estilos!

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


La conversión de código publicada por Sparkie es poco fiel a la conversión de C #, ya que la propiedad B no es virtual y no está directamente expuesta en instancias del tipo Mi (debe enviar las instancias a IB para acceder a B). Aquí hay una conversión más fiel:

[<AbstractClass>] type ABase() = abstract A: string type IB = abstract B: string type My(a, b) = inherit ABase() override this.A = a abstract B: string default this.B = b interface IB with member this.B = this.B

Una cosa a tener en cuenta si eres nuevo en F #: la referencia pasada a las implementaciones de los miembros de la interfaz es del tipo de la clase declarante, por lo que (en lo anterior) la implementación de B de IB hace referencia a la propiedad B declarada en la clase.


Otra comparación.

usando palabra clave:

F#

use x = X() use y = Y() use z = Z() ...

Nemerle:

using (o = SomeType()) ... using (x = X()) using (y = Y()) using (z = Z()) ...

Formato de cadena:

F#:

let a = 1 let b = "x" sprintf "%d %s" (a + 1) b

Nemerle:

def a = 1; def b = "x"; def c = Nemerle.IO.sprintf("%d %s", a + 1, b);

Interpolación de cadenas:

Tiene F # esta característica?

Nemerle:

def a = 1; def b = "x"; def c = $"$(a + 1) - $b"; // 0 - x

palabra clave de foreach: F #:

let list1 = [ 1; 5; 100; 450; 788 ] for i in list1 do printfn "%d" i let list1 = [ 1; 5; 100; 450; 788 ] Seq.iteri (printfn "%d - %d") let list1 = [ 1; 5; 100; 450; 788 ] Seq.iteri (fun i x -> if i > 10 then printfn "%d - %d" i x) let list1 = [ 1; 5; 100; 450; 788 ] for x in list1 do printf "%s" (if x > 10 then printf "A" elif x < 10 then printf "B" elif "C"

Nemerle:

def list1 = [ 1, 5, 100, 450, 788 ] foreach (i in list1) WriteLine($"$i") def list1 = [ 1, 5, 100, 450, 788 ] foreach (i in list1 with index) // index has an index of item. works for any collection. WriteLine($"$index - $i") def list1 = [ 1, 5, 100, 450, 788 ] foreach (i when i > 10 in list1 with index) // Pattern in foreach WriteLine($"$index - $i") def list1 = [ 1, 5, 100, 450, 788 ] foreach (_ in list1) // Implicit match | i when i > 10 => WriteLine("A") | i when i < 10 => WriteLine("B") | _ => WriteLine("C") foreach (_ in []) | i when i > 10 => WriteLine("A") | i when i < 10 => WriteLine("B") | _ => WriteLine("C") otherwise // Otherwise is taken when there were no iterations WriteLine("X")


si te gustan los dos idiomas, ¿por qué no mezclarlos? Puede construir algunas bibliotecas en Nemerle usando macros y otras en F # para todo lo demás. ¿No es esa una de las ventajas de utilizar el tiempo de ejecución administrado? :RE

A continuación, puede compilar la IU (si corresponde) utilizando un lenguaje más conveniente para ella, como C # o VB.NET.

Y una vez que decida agregar soporte de scripting a su producto, ¡puede usar IronPython / IronRuby!

Tengo entendido que la compatibilidad con IDE de Nemerle solo funciona en VS2008 (pero es compatible con .net3.5 y 4.0) y F # funciona tanto en VS2010 como en VS2008, por lo que además de las diferencias de sintaxis, cree que su decisión también debe considerar eso, que IDE estarás usando


muéstrame si es real para hacer esto fácil en F #, C # también si ves que lo hago mal porque no estoy seguro sobre otras formas de hacer claramente lo mismo.

Sus ejemplos F # y C # no son muy concisos. Vamos a reescribir algunos de los ejemplos en su OP:

La coincidencia de patrones

Nemerle:

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

F#:

No estoy 100% seguro de lo que está haciendo el código, pero parece que está basado en esta respuesta . No creo que sea fácil crear nuevas variables en una expresión de coincidencia, pero creo que los patrones activos son excesivos.

Escribiría el código así:

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

Los mapas también funcionan:

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

I <3 La sugerencia de Jon también:

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

Archivos

Nemerle:

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

F#:

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

DO#:

class X { public string A { get; private set; } public string B { get; private set; } public X(string a, string b) { A = a; B = b; } }

Clases

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

Algunas cosas a tener en cuenta aquí:

  • Las interfaces F # se definen utilizando la palabra clave abstract . Puede convertirlos en clases abstractas usando el atributo [<AbstractClass>] .

  • Las interfaces se implementan explícitamente. En general, debe lanzar un objeto a una definición de interfaz para invocar miembros de la interfaz: let x = My("a", "b"); printf "%s" (x :> IB).B let x = My("a", "b"); printf "%s" (x :> IB).B . Para evitar el reparto, debe crear miembros públicos que reflejen sus métodos de interfaz.

  • Las funciones virtuales definen un abstract member con una implementación default .

Une todos estos componentes y obtiene definiciones de clase que son perjudiciales para los nervios oculares. Pero está bien, ya que las clases generalmente no se usan con mucha frecuencia. La mayoría de los modelos de objetos F # se definen a través de uniones y registros; donde se usan las clases, las jerarquías de clase son muy planas en lugar de profundas, por lo que no verá la herencia o las funciones virtuales utilizadas con tanta frecuencia en F # que en C #.

DO#:

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; } }

Para resumir, creo que F # es bastante comparable a Nemerle, pero parece que lo estás aprendiendo. No se preocupe, cuando estaba aprendiendo F #, estaba escribiendo un código feo y voluminoso que básicamente reflejaba C # con una sintaxis más funky. Tardé un poco antes de poder escribir más idiomáticamente.

Recomiendo lo siguiente:

  • Si está familiarizado con Nemerle y le gusta usarlo, continúe haciéndolo :)
  • Si quieres aprender F #, creo que tu proyecto es un buen lugar para comenzar. Creo que puedes escribir F # tan limpio o mejor que tu Nemerle.
  • C # también está bien, pero no lo preferiría en ninguno de los dos idiomas si está haciendo una gran coincidencia de patrones o procesando símbolos.