Al usar Python, ¿cuáles son los beneficios de escribir su API de servidor web back-end con Golang o Haskell? ¿Es recomendable?

No está claro en la redacción de su pregunta si tiene la intención de acceder a la API a través de llamadas en red (una API REST, por ejemplo), o como llamadas a la biblioteca a través de una interfaz de función externa. La distinción hace una diferencia importante en el análisis de costo-beneficio.

En cualquier caso, los beneficios son la velocidad, robustez bajo carga de escala y capacidades de concurrencia mucho mejores ofrecidas por cualquiera de los idiomas.

En cualquier caso, hay un costo en la interfaz entre Python y el otro idioma.

  • Debe obtener la abstracción correcta y tener la separación correcta de preocupaciones
  • Debe tener muy claro qué idioma / componente administra el estado.
  • Los datos pasados ​​a través de la interfaz tienen un costo, en comparación con el paso de datos en un idioma. Es más complicado de administrar, puede requerir conversión y / o formatos intermedios, amenaza con ser un cuello de botella de red / memoria / recurso.

En cualquier caso, los costos de usar dos idiomas en un proyecto incluyen

  • Se requiere una base de habilidades más amplia (más personas o más personas altamente calificadas o una combinación de ambas)
  • Más complejo de soportar (herramientas, pruebas, etc.)

Red de comunicacion

Si su arquitectura es una aplicación Python que se comunica con este componente de back-end a través de algún protocolo de red, otro beneficio es la arquitectura realmente escalable. Hay muchas historias exitosas de Internet (Twitter, Facebook y más) en las que los componentes que se muestran al público están en un lenguaje dinámico (por los rápidos beneficios de desarrollo y los ricos marcos web disponibles) y los componentes de back-end están en un alto rendimiento mecanografiado estáticamente idioma.

Interfaz de la biblioteca

Escribir una biblioteca de Python principalmente en otro idioma es definitivamente una cosa. Numpy está escrito principalmente en C, con Python proporcionando solo las abstracciones de nivel superior.

Dicho eso

  1. Numpy claramente proporciona algo (funciones matemáticas complejas) para las cuales Python nativo es una herramienta pobre.
  2. Como Terry Lambert ha detallado, C es mucho más simple llamar desde Python que Go o Haskell. El costo de una interfaz Python to Go / Haskell es más alto (aunque ambos tienen una historia de concurrencia para ofrecer que C no).

Ahora, no usaría Python para escribir el núcleo de un servidor web. Pero escribir bibliotecas centrales para su servidor web en otro idioma no resuelve un problema crucial para un componente de servidor web, que mencioné en la primera parte de mi respuesta: estado. La concurrencia o el subprocesamiento múltiple son esenciales para los servicios web y, por lo tanto, no son la fortaleza de Python. Pero si el otro idioma solo proporciona una biblioteca, entonces

  1. Python realmente posee el estado. No es una victoria
  2. La biblioteca permite el arranque de un motor de servidor web administrado por el tiempo de ejecución del otro idioma, con llamadas de Python que se comunican a través de algún mecanismo de IPC.

La opción 1 es tonta. La opción 2 es factible, especialmente si puede registrar devoluciones de llamada en código Python. Pero se parece mucho a la cola que mueve al perro. Creo que sería más sensato escribir un componente de servidor web que podría llamar a Python por algunas cosas, que al revés. Deje que el servidor web llame a Python por el rico conjunto de abstracción en que Python es bueno. No haga que Python intente administrar algo que no entiende.

Resumen

Tiene que hacer un análisis complejo de costo-beneficio y realmente tiene que justificar los costos. La mayoría de los proyectos que mezclan Python o Ruby con un lenguaje estático no comenzaron de esa manera; hicieron todo en el lenguaje dinámico hasta que tuvieron un producto comprobado que tuvo que ampliarse, momento en el que invirtieron en el otro idioma.

Nota:

Tengo que estar en desacuerdo, hasta cierto punto, con Terry Lambert cuando dice “Go está destinado a encajar en el mismo nicho de programación de back-end del servidor que Python en primer lugar”. Ese es un nicho donde los casos de uso de Python y Go se superponen (con Python también ocupan otros roles) y llenan ese nicho de maneras muy diferentes. Go tiene el rendimiento en bruto, la seguridad de tipo y la historia de concurrencia, pero Python lo supera en términos de abstracción. Go es en realidad anti-abstracción, tanto es así que copió el bucle for de Python, mantuvo los peores bits (la sintaxis y la semántica inconsistentes en diferentes tipos) y tiró la mayoría de los bits buenos.

Entonces puedo ver un caso para usar Go para los bits de rendimiento y Python para el resto. Pero haga que separen los componentes que se comunican a través de los enchufes o ponga Go en el asiento del conductor, con Python como extensión.

Realmente no hay ninguno.

Las llamadas externas de Python a otro código se realizan utilizando el lenguaje C ABI, ya que la mayoría de las bibliotecas externalizan un lenguaje C ABI.

Hasta aquí todo bien.

Las llamadas externas de Golang y Haskell a otro código también se realizan utilizando el lenguaje C ABI.

Además: hasta ahora, todo bien.

Llamar desde Haskell a otro código es algo complicado, ya que requiere que escriba los enlaces del lenguaje Haskell para el código C en Haskell y realice conversiones de tipo.

Pero para Go, debe tener funciones de contenedor similares, utilizando “cgo”.

Hasta ahora, todo bien … es factible llamar a los tres lenguajes al código C.


¿Cómo hace que el código Go se vea como un C ABI, para que pueda llamar desde el código C al código Go?

Resulta que tiene que hacer una devolución de llamada, utilizando un contenedor que pasa un contenedor a la función Go.

No puedes llamarlo directamente.


¿Cómo hace que el código de Haskell se parezca a un C ABI, para que pueda llamar desde el código C al código Haskell?

Debe hacer algunas cosas bastante complicadas en C para hacer llamadas a Haskell, y debe hacer cosas similares en el código de Haskell para hacer una función de código auxiliar utilizando tipos de C que puedan llamar a otro código de Haskell.

No puedes llamarlo directamente.


Lo que esto significa en el límite es que tienes que escribir mucho código de pegamento para hacer lo que quieres hacer.

Esto está sobrecargado, y va a consumir parte de su rendimiento cruzando estos envoltorios dentro y fuera del C ABI para hacer las llamadas desde Python a Haskell o Go.

El proceso general de proporcionar estos contenedores se denomina “creación de enlaces de idiomas”.


Es factible, pero …

Go está destinado a encajar en el mismo nicho de programación de back-end del servidor que Python en primer lugar.

Otro que tratar de compensar una deficiencia en las bibliotecas disponibles en Go utilizando Python en su lugar, en lugar de hacer The Right Thing ™, y proporcionar versiones Go de las bibliotecas que están presentes en Python, pero que faltan en Go, porque Go es un lenguaje realmente inmaduro: en realidad no estás proporcionando ningún beneficio esencial en el back-end al tener Python allí, en lugar de solo usar Go.

La implementación de doble devolución de llamada necesaria para llamar a Go desde Python (a través de C ABI) es extremadamente fea.

Haskell es un caballo de una distancia entre ejes diferente.

Si eso no tiene sentido, es porque es una metáfora mixta, a propósito, para demostrar que estás mezclando metáforas de programación.

Tiene mucho más sentido querer llamar a Haskell desde Python. O de Go, para el caso.

Haskell es un lenguaje funcional y, por lo tanto, no tiene mucho soporte de framework web. Mucho de lo que hay allí no ha sido tocado durante varios años (creo que GHC 7.8.3 fue la última vez), porque tienes que ser un fanático de Haskell para querer escribir todo en el idioma.

La implementación de doble capa de envoltorios ABI necesaria para llamar a Haskell desde Python (a través del C ABI) es extremadamente fea.


Para escribir este código de pegamento, necesitarás un unicornio.

La persona que escribe este código de pegamento, si sigue adelante con su plan, tendrá que estar bastante familiarizado o ser capaz de hacerlo, con Python, C y Haskell; o Python, C y Go.

Dicho esto … desde una perspectiva comercial, aún podría tener sentido que hagas esto, pero solo porque:

  • Los programadores de Python son realmente baratos; probablemente sean menos costosos de contratar
  • Los programadores C son más caros de contratar
  • Los programadores de Go son aún más caros, porque Go es un lenguaje relativamente inmaduro, pero tiene una curva de aprendizaje baja y un grupo más grande de personas dispuestas a aprenderlo porque ven cualquier cosa respaldada por Google como “el futuro”.
  • Los programadores de Haskell son bastante caros de todos ellos, porque hay menos y porque tienen tanta demanda en inteligencia artificial / aprendizaje automático, ¿pero las cosas interesantes que puede hacer en esos dominios problemáticos? Con buena pinta…

Entonces, desde una perspectiva de contención de costos, podría tener sentido escribir todo lo que pueda en Python usando programadores baratos para contratar, y luego comprar un unicornio (o entrenar a uno) y hacer el otro extremo de las cosas en Haskell (o Go )


¿Personalmente?

Probablemente me saltaría Python for Go hasta el final, si quisiera usar Go en el back-end.

Si necesita el Haskell, puede llamarlo desde Go (como si fuera un código C) tan fácilmente como podría llamarlo desde Python (como si fuera un código C).

Por supuesto, tu unicornio Go / C / Haskell podría ser más caro.

¿Cómo lo manejaría en tus zapatos?

Escribiría un IDL ( lenguaje de descripción de interfaz) y un compilador, y una biblioteca XDR ( representación de datos externos ) para Python, y una para Go y otra para Haskell.

Y luego definiría la interfaz que quería en el IDL, como algo abstracto.

Y luego ejecutaba el compilador con las banderas de los idiomas que quería hablar entre ellos, y escupía el código de pegamento para mí, en lugar de confiar en un humano (defectuoso) para hacer el trabajo a mano por cada interfaz

Y luego vincularía el código de pegamento con las bibliotecas XDR apropiadas, y tendría el código que necesitaba, ya preparado.

Pero solo soy yo.

Tengo que estar en desacuerdo con Terry Lambert. Hay algunas características de lenguaje para cada Haskell, Python y Go que hacen que cada idioma sea ventajoso o desventajoso de alguna manera.

Sin embargo, estoy de acuerdo con él y con todos los demás en este punto: si el resto de su servidor web está escrito en Python, por amor de Dios, escriba la API en ese servidor en Python. No vayas a complicar las cosas solo porque algo se ve nuevo y brillante. No puedo pensar en una sola razón para escribir la API en un servidor en un idioma que no sea el que está escrito en el servidor. Las piezas críticas de rendimiento de su servidor no deberían ser parte de la API. Si es así, probablemente no esté modularizando adecuadamente su código lo suficientemente bien y necesite refactorizarlo. Si necesita rendimiento en Python, vaya a C.

Ahora, con eso fuera del camino, hablando de en qué idioma escribir un servidor, en general …

Si se trata de un sitio de carga normal que no es demasiado grande o complejo (su aplicación web promedio), continúe y use Python.

Sin embargo, Go tiene la ventaja de ser un lenguaje “seguro para la memoria”. No soy un completo experto en Go, por lo que no diré que las pérdidas de memoria son imposibles en Go, pero el lenguaje lo protege mucho de escribirlas. Go también tiene un buen sistema de tipos y tiene un rendimiento estelar. Entonces, aunque Python se une mucho a C, esto aún lo expondrá a las trampas de la administración de memoria de estilo C. Si cree que va a escribir una gran cantidad de algoritmos personalizados que necesitan un aumento en el rendimiento, o si realmente necesita resistencia y no puede soportar tener incluso pequeñas pérdidas de memoria por cualquier razón (es probable que necesite que el servicio se ejecute con recursos limitados para sea ​​cual sea el motivo, o de lo contrario, debe ser capaz de mantenerse con vida tanto tiempo que una pequeña pérdida de memoria podría ocasionar la desaparición de GB de RAM con el tiempo y no se puede simplemente reiniciar el servicio una vez cada pocas semanas), entonces GoLang es una buena opción. GoLang también tiene un mejor modelo de concurrencia que Python, por lo que si va a haber una gran concurrencia, con una gran cantidad de mensajes que pasan, en lugar de trabajar alrededor de los límites de canalizar datos entre hilos en Python (o peor, compartir el estado) , usa Go. Go también es un idioma realmente fácil de aprender. Si te encuentras alcanzando C, considera ir. Pude ser productivo en Go después de solo 2 horas de leer sobre el idioma y jugar con él.

Haskell es un lenguaje difícil de justificar. Vale la pena aprender lo que le enseñará, pero en la práctica, no mucha gente lo sabe, es difícil contratarlo, y las bibliotecas en Haskell son relativamente inmaduras y tienden a abandonarse después de un tiempo. Entonces, Haskell, a pesar de todo el alboroto al respecto, normalmente no es una gran opción (y esto viene de alguien que cree, en un mundo ideal, que todo se ejecutaría en un idioma como Haskell o Idris). Sin embargo, hay un área que es una clara victoria para Haskell: si no puede tener ningún error sin importar qué y esto es más importante que la necesidad de soportar sus propias bibliotecas para hacer las cosas, el sistema de tipos de Haskell es muy flexible y robusto y lo hará le brinda la validación de su código en tiempo de compilación más que cualquier otro lenguaje de programación (popular) existente. Puede escribir código estúpido en Haskell, pero es mucho más difícil, y el código más tonto en Haskell no se acerca al código más tonto en C, Java, Go, Python, etc. porque el lenguaje realmente lo protege de su propia estupidez y ignorancia. Sin embargo, si su caso es que necesita absolutamente la seguridad de tipo para eliminar errores, pero esto no es mucho más importante que la madurez de la biblioteca y el apoyo de la comunidad, entonces Rust lo atenderá mejor para aplicaciones de bajo nivel o más basadas en el rendimiento, y Scala para aplicaciones de nivel superior si necesita poder contratar más fácilmente (puede escribir Scala en un estilo Java, lo que abre un poco sus opciones de contratación). Para Haskell, primero piense: ¿pueden Rust o Scala ser lo suficientemente buenos? Si la respuesta es ‘no’, entonces adelante. Haskell sería bueno para el software de misión crítica donde la falla es de alto riesgo, como automatizar un transbordador espacial o validar transacciones financieras y seguridad.

Terry Lambert dio una excelente respuesta (excepto que Haskell no es tan malo para Web, pero eso no viene al caso). Definitivamente no es recomendable. En general, mezclar idiomas en un solo proyecto es algo que no debe hacerse a la ligera (bueno, excepto si estamos hablando de back-end en un idioma y front-end en otro, está perfectamente bien).

Sin embargo, puede haber algunas razones para hacerlo. Como ejemplo abstracto, imagine que hay una biblioteca en, digamos, Haskell que hace exactamente lo que quiere hacer, pero no hay nada similar en Python. Además, implementarlo desde cero será propenso a errores, requerirá mucho tiempo y, en general, no será agradable. En ese caso, podría ser más fácil hacer lo que necesita hacer en Haskell (ya que tiene una biblioteca) y escribir el resto de la aplicación en Python alrededor de eso.

Dicho esto, probablemente sea mejor evitar C ABI y, en su lugar, usar un patrón de microservicio o algo similar, aunque puede ser relativamente fácil pasar datos entre dos procesos (a través de cualquier patrón RPC conveniente), rápidamente se vuelve horrible y complicado al intentar obtener dos diferentes los idiomas juegan bien dentro de un solo proceso. Podría ser más fácil si uno de esos lenguajes es C, pero no tanto, francamente.

Otra razón para mezclar lenguajes podría ser el rendimiento, pero en general sería mucho más fácil crear una biblioteca C ++ para partes críticas para el rendimiento y luego conectarla a Python. Ni Go ni Haskell tienen mucho sentido en este caso. Ir es agradable, pero al mismo tiempo es demasiado bajo y no lo suficientemente bajo, al menos para mi gusto. Y Haskell generalmente no es conocido por su rendimiento (está bien, pero no hay nada que destacar).

Mientras estoy en el tema, tengo que decir que uno debe evitar la optimización prematura. Una buena idea es obtener primero un software que funcione , perfilar su rendimiento y luego optimizar los cuellos de botella. Optar por C ++ desde el principio probablemente sería un error.

El beneficio principal vendría en el rendimiento, en particular el golang, se creó específicamente con la creación de servicios de alto rendimiento en mente. Golang es un lenguaje nativamente compilado, fuertemente tipado y como resultado es mucho más rápido que Python.

Esto no hará mucha diferencia en la práctica, ya que su aplicación es relativamente pequeña y solo debe admitir una pequeña cantidad de usuarios; pero si necesita escalar a 100 o 1000 de transacciones por segundo, ir representará una mejora significativa en términos de rendimiento.

En una de las aplicaciones para las que soy el CTO, estamos reemplazando gradualmente los servicios clave originalmente escritos en PHP con servicios de golang … y el ahorro de costos en los recursos del servidor solo ha pagado el trabajo de desarrollo.

Verificación de tipos, posiblemente lenguajes más robustos para manejar las cargas de estrés mejor y también, posiblemente, más agradable de codificar (relacionado con la verificación de tipos).