f# module code-organization static-members

Organización del código F#: tipos y módulos



module code-organization (4)

¿Cómo se decide entre escribir una función dentro de un módulo o como un miembro estático de algún tipo?

Por ejemplo, en el código fuente de F #, hay muchos tipos que se definen junto con un módulo con el mismo nombre, de la siguiente manera:

type MyType = // ... [<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>] module MyType = // ...

¿Por qué simplemente no define las operaciones como miembros estáticos de tipo MyType?


Además de las otras respuestas, hay un caso más para usar módulos:

Para los tipos de valor, pueden ayudar a definir propiedades estáticas que no se vuelven a evaluar cada vez que se accede a ellas. por ejemplo:

type [<Struct>] Point = val x:float val y:float new (x,y) = {x=x;y=y} static member specialPoint1 = // sqrt is computed every time the property is accessed Point (sqrt 0.5 , sqrt 0.5 ) [<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>] module Point = let specialPoint2 = // sqrt is computed only once when the Module is opened Point (sqrt 0.5 , sqrt 0.5 )


Algunas grandes distinciones que no fueron mencionadas originalmente:

  • Las funciones son valores de primera clase en F #, pero los miembros estáticos no lo son. Así que puedes escribir objs |> Seq.map Obj.func pero no puedes escribir objs |> Seq.map Obj.Member .

  • Las funciones pueden ser curry, pero los miembros no pueden.

  • El compilador inferirá tipos automáticamente cuando llame a una función, pero no cuando invoque a un miembro. Así que puedes escribir let func obj = obj |> Obj.otherFunc pero no puedes escribir let func obj = obj.Member .

Dado que los miembros son más restringidos, generalmente uso funciones a menos que explícitamente quiera admitir OOP / C #.


Aquí hay algunas notas sobre las distinciones técnicas.

Los módulos pueden ser ''abiertos'' (a menos que tengan RequireQualifiedAccessAttribute). Es decir, si pones las funciones ( F y G ) en un módulo ( M ), entonces puedes escribir

open M ... F x ... G x ...

mientras que con un método estático, siempre escribirías

... M.F x ... M.G x ...

Las funciones del módulo no pueden ser sobrecargadas . Las funciones en un módulo están vinculadas a let, y las funciones limitadas de let no permiten la sobrecarga. Si quieres poder llamar a los dos

X.F(someInt) X.F(someInt, someString)

debe utilizar member de un tipo, que solo funcionan con llamadas "calificadas" (por ejemplo, type.StaticMember(...) u object.InstanceMember(...) ).

(¿Hay otras diferencias? No puedo recordar.)

Esas son las principales diferencias técnicas que influyen en la elección de uno sobre el otro.

Además, hay cierta tendencia en el tiempo de ejecución de F # (FSharp.Core.dll) a usar módulos solo para tipos específicos de F # (que normalmente no se usan al interoperar con otros lenguajes .Net) y métodos estáticos para API que son más lenguajes. -neutral. Por ejemplo, todas las funciones con parámetros al curry aparecen en módulos (las funciones al curry no son triviales para llamar desde otros idiomas).


En F #, prefiero un miembro estático en un tipo sobre una función en un módulo si ...

  1. Tengo que definir el tipo independientemente del miembro.
  2. El miembro está funcionalmente relacionado con el tipo que estoy definiendo.